@platforma-sdk/pl-cli 0.10.3 → 0.10.5

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/cli.js CHANGED
@@ -1,11 +1,11 @@
1
- import { _ as e, a as t, b as n, c as r, d as i, f as a, g as o, h as s, i as c, l, m as u, n as d, o as f, p, r as ee, s as te, t as m, u as h, v as g, x as ne, y as _ } from "./output-BAuWMUzX.js";
1
+ import { _ as e, a as t, b as n, c as r, d as i, f as a, g as o, h as s, i as c, l, m as u, n as d, o as f, p, r as ee, s as te, t as m, u as h, v as g, x as ne, y as _ } from "./output-D41Wn0wM.js";
2
2
  import { Command as v, InvalidArgumentError as y, Option as b } from "commander";
3
3
  import { asSignedResourceId as x, createSignedResourceId as S, field as C, isLocalResourceId as re, isNotNullSignedResourceId as w, resourceIdFromString as T, resourceIdToString as E, resourceType as D, resourceTypeToString as O } from "@milaboratories/pl-client";
4
4
  import { randomUUID as k } from "node:crypto";
5
5
  import { ProjectMetaKey as A } from "@milaboratories/pl-middle-layer";
6
6
  import { createInterface as j } from "node:readline";
7
7
  import { once as M } from "node:events";
8
- //#region src/base_command.ts
8
+ // src/base_command.ts
9
9
  async function N(e) {
10
10
  return e.adminUser && e.adminPassword ? n({
11
11
  address: e.address,
@@ -27,8 +27,7 @@ async function P(e) {
27
27
  projectListRid: s
28
28
  };
29
29
  }
30
- //#endregion
31
- //#region src/cmd-opts.ts
30
+ // src/cmd-opts.ts
32
31
  function F(e, ...t) {
33
32
  for (let n of t) for (let t of n) e.addOption(t);
34
33
  return e;
@@ -38,8 +37,7 @@ var I = () => [new b("-a, --address <url>", "Platforma server address").env("PL_
38
37
  new b("--admin-password <password>", "Admin/controller password").env("PL_ADMIN_PASSWORD"),
39
38
  new b("--target-user <user>", "Operate on this user's data (requires admin credentials)").env("PL_TARGET_USER")
40
39
  ];
41
- //#endregion
42
- //#region src/cmd/project/list.ts
40
+ // src/cmd/project/list.ts
43
41
  function V() {
44
42
  let e = new v("list").description("List all projects for the authenticated user.");
45
43
  return F(e, L(), R(), B()), e.action(async (e) => {
@@ -77,8 +75,7 @@ function V() {
77
75
  }
78
76
  }), e;
79
77
  }
80
- //#endregion
81
- //#region src/cmd/project/info.ts
78
+ // src/cmd/project/info.ts
82
79
  function H() {
83
80
  let e = new v("info").description("Show detailed information about a project.");
84
81
  return e.argument("<project>", "Project ID or label"), F(e, L(), R(), B()), e.action(async (e, n) => {
@@ -104,8 +101,7 @@ function H() {
104
101
  }
105
102
  }), e;
106
103
  }
107
- //#endregion
108
- //#region src/cmd/project/duplicate.ts
104
+ // src/cmd/project/duplicate.ts
109
105
  function U() {
110
106
  let e = new v("duplicate").description("Duplicate a project within the same user. Auto-renames on collision by default.");
111
107
  return e.argument("<project>", "Project ID or label"), F(e, L(), R(), B()), e.option("-n, --name <name>", "Name for the duplicate"), e.addOption(new b("--auto-rename", "Auto-rename on collision (default: true)").default(!0)), e.option("--no-auto-rename", "Fail on name collision instead of auto-renaming"), e.action(async (e, n) => {
@@ -136,8 +132,7 @@ function U() {
136
132
  }
137
133
  }), e;
138
134
  }
139
- //#endregion
140
- //#region src/cmd/project/rename.ts
135
+ // src/cmd/project/rename.ts
141
136
  function W() {
142
137
  let e = new v("rename").description("Rename a project.");
143
138
  return e.argument("<project>", "Project ID or label"), F(e, L(), R(), B()), e.addOption(new b("-n, --name <name>", "New name for the project").makeOptionMandatory()), e.action(async (e, n) => {
@@ -154,8 +149,7 @@ function W() {
154
149
  }
155
150
  }), e;
156
151
  }
157
- //#endregion
158
- //#region src/cmd/project/delete.ts
152
+ // src/cmd/project/delete.ts
159
153
  function G() {
160
154
  let e = new v("delete").description("Delete a project. This permanently destroys all project data.");
161
155
  return e.argument("<project>", "Project ID or label"), F(e, L(), R(), B()), e.option("--force", "Skip confirmation", !1), e.action(async (e, n) => {
@@ -188,8 +182,7 @@ function G() {
188
182
  }
189
183
  }), e;
190
184
  }
191
- //#endregion
192
- //#region src/cmd/admin/copy-project.ts
185
+ // src/cmd/admin/copy-project.ts
193
186
  function K() {
194
187
  let e = new v("copy-project").description("Copy a project from one user to another. Requires admin/controller credentials.");
195
188
  return F(e, L(), z()), e.addOption(new b("--source-user <user>", "Username of the source project owner").makeOptionMandatory()), e.addOption(new b("--source-project <project>", "Source project ID or label").makeOptionMandatory()), e.option("--target-user <user>", "Username of the target user (defaults to source-user for same-user copy)"), e.option("-n, --name <name>", "Name for the copied project"), e.addOption(new b("--auto-rename", "Auto-rename on collision (default: true)").default(!0)), e.option("--no-auto-rename", "Fail on name collision instead of auto-renaming"), e.action(async (e) => {
@@ -222,8 +215,7 @@ function K() {
222
215
  }
223
216
  }), e;
224
217
  }
225
- //#endregion
226
- //#region src/cmd/admin/user-list.ts
218
+ // src/cmd/admin/user-list.ts
227
219
  function q() {
228
220
  let e = new v("user-list").description("List all known users' logins from a live server. Requires admin/controller credentials.");
229
221
  return F(e, L("csv", [
@@ -240,8 +232,7 @@ function q() {
240
232
  }
241
233
  }), e;
242
234
  }
243
- //#endregion
244
- //#region src/cmd/admin/delete-user.ts
235
+ // src/cmd/admin/delete-user.ts
245
236
  function J() {
246
237
  let n = new v("delete-user").description("Delete a user account. Optionally re-homes the user's projects to another user first. Requires admin/controller credentials.");
247
238
  return n.argument("<user>", "Username of the account to delete"), F(n, L(), z()), n.addOption(new b("--move-projects-to <user>", "Re-home the deleted user's projects to this user before deleting the account")), n.addOption(new b("--delete-projects", "Delete the user's projects along with the account, losing their data")), n.option("--force", "Skip confirmation", !1), n.action(async (n, r) => {
@@ -258,8 +249,8 @@ function J() {
258
249
  }
259
250
  let u = [];
260
251
  if (r.moveProjectsTo && o.length > 0) {
261
- let t = await l(i, (await e(i, r.moveProjectsTo)).userRoot);
262
- u = await s(i, a.projectListRid, t);
252
+ let t = await e(i, r.moveProjectsTo), n = await l(i, t.userRoot);
253
+ u = await s(i, a.projectListRid, n);
263
254
  }
264
255
  let d = a.projectListRid === void 0 ? [] : await p(i, a.projectListRid);
265
256
  Y(n, r, o, d);
@@ -316,8 +307,7 @@ function ae(e, t, n, r) {
316
307
  e.targetLabel
317
308
  ])))) : i.push(`User ${e} had no projects to move.`) : r.length > 0 && (i.push(`Deleted ${r.length} project(s) belonging to ${e}:`), i.push(r.map((e) => ` - ${e.label}`).join("\n"))), i.push(`Deleted user "${e}".`), i.join("\n");
318
309
  }
319
- //#endregion
320
- //#region src/resource_id.ts
310
+ // src/resource_id.ts
321
311
  function X(e) {
322
312
  let t = e.trim();
323
313
  if (t.includes("|")) return x(t);
@@ -325,8 +315,7 @@ function X(e) {
325
315
  if (n === void 0 && /^\d+$/.test(t) && (n = BigInt(t)), n === void 0 || n === 0n || re(n)) throw Error(`not a global resource ID: ${e}`);
326
316
  return S(n);
327
317
  }
328
- //#endregion
329
- //#region src/cmd/maintenance/list-resources.ts
318
+ // src/cmd/maintenance/list-resources.ts
330
319
  function oe() {
331
320
  let e = new v("list-resources").description("List resources of one type as JSON lines, in ascending order of resource ID. Resource data and key-value values are printed as JSON when they are valid JSON, as text when they are valid UTF-8, and as {\"base64\": ...} in other cases. Requires admin/controller credentials.");
332
321
  return F(e, I(), z()), e.addOption(new b("--type <name:version>", "Resource type to list (e.g. 'Blob/work:1')").argParser(le).makeOptionMandatory()), e.addOption(new b("--limit <n>", "Maximum number of resources to list. 0 lists all resources").argParser(ue).default(0)), e.addOption(new b("--after <resource-id>", "List only resources with IDs greater than this ID. To get the next page, give the ID of the last resource of the previous page").argParser(X)), e.option("--load-fields", "Load the fields and the error of each resource ('fields' and 'error' keys)", !1), e.option("--load-kv", "Load the key-value entries of each resource ('kv' key)", !1), e.action(async (e) => {
@@ -406,8 +395,7 @@ function ue(e) {
406
395
  if (!Number.isInteger(t) || t < 0) throw new y(`not a non-negative integer: ${e}`);
407
396
  return t;
408
397
  }
409
- //#endregion
410
- //#region src/cmd/maintenance/set-resource-error.ts
398
+ // src/cmd/maintenance/set-resource-error.ts
411
399
  var de = "Manual error set by administrator to trigger blocks re-run. Run block once again to get rid of it.";
412
400
  function fe() {
413
401
  let e = new v("set-resource-error").description("Put an error on one or more resources in a single transaction. Resources that already have an error are skipped. Requires admin/controller credentials.");
@@ -438,8 +426,7 @@ function fe() {
438
426
  }
439
427
  }), e;
440
428
  }
441
- //#endregion
442
- //#region src/cli.ts
429
+ // src/cli.ts
443
430
  function $() {
444
431
  let e = new v();
445
432
  e.name("pl-cli").description("Platforma command-line client");
@@ -453,7 +440,6 @@ function $() {
453
440
  async function pe(e = process.argv) {
454
441
  await $().parseAsync(e);
455
442
  }
456
- //#endregion
457
443
  export { $ as buildProgram, pe as run };
458
444
 
459
445
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","names":[],"sources":["../src/base_command.ts","../src/cmd-opts.ts","../src/cmd/project/list.ts","../src/cmd/project/info.ts","../src/cmd/project/duplicate.ts","../src/cmd/project/rename.ts","../src/cmd/project/delete.ts","../src/cmd/admin/copy-project.ts","../src/cmd/admin/user-list.ts","../src/cmd/admin/delete-user.ts","../src/resource_id.ts","../src/cmd/maintenance/list-resources.ts","../src/cmd/maintenance/set-resource-error.ts","../src/cli.ts"],"sourcesContent":["import type { PlClient, SignedResourceId } from \"@milaboratories/pl-client\";\nimport { createPlConnection, createAdminPlConnection } from \"./connection\";\nimport { getProjectListRid, navigateToUserRoot } from \"./project_ops\";\n\n/**\n * Low-level: get an authenticated PlClient without resolving a project list.\n * Use this for commands that navigate to multiple users (e.g. admin copy-project).\n *\n * Callers own the returned client and must `await pl.close()` when done.\n */\nexport async function connectClient(flags: {\n address: string;\n user?: string;\n password?: string;\n adminUser?: string;\n adminPassword?: string;\n}): Promise<PlClient> {\n if (flags.adminUser && flags.adminPassword) {\n return createAdminPlConnection({\n address: flags.address,\n adminUser: flags.adminUser,\n adminPassword: flags.adminPassword,\n });\n }\n return createPlConnection({\n address: flags.address,\n user: flags.user,\n password: flags.password,\n });\n}\n\n/**\n * Connect and resolve the project list for a single user.\n * In admin mode (--admin-user + --admin-password + --target-user), operates on the target user's data.\n * In user mode, operates on the authenticated user's own data.\n *\n * Callers own the returned client and must `await pl.close()` when done.\n */\nexport async function connect(flags: {\n address: string;\n user?: string;\n password?: string;\n adminUser?: string;\n adminPassword?: string;\n targetUser?: string;\n}): Promise<{ pl: PlClient; projectListRid: SignedResourceId }> {\n const hasAdminUser = !!flags.adminUser;\n const hasAdminPassword = !!flags.adminPassword;\n const hasTarget = !!flags.targetUser;\n\n // Validate flag combinations\n if (hasTarget && !(hasAdminUser && hasAdminPassword)) {\n throw new Error(\"--target-user requires --admin-user and --admin-password\");\n }\n if ((hasAdminUser || hasAdminPassword) && !hasTarget) {\n throw new Error(\"--admin-user/--admin-password require --target-user for project commands\");\n }\n\n const pl = await connectClient(flags);\n\n let projectListRid: SignedResourceId;\n if (hasTarget) {\n const nav = await navigateToUserRoot(pl, flags.targetUser!);\n projectListRid = nav.projectListRid;\n } else {\n projectListRid = await getProjectListRid(pl);\n }\n\n return { pl, projectListRid };\n}\n","import { Command, Option } from \"commander\";\nimport type { OutputFormat } from \"./output\";\n\n/** Add one or more groups of options to a command, preserving group order. */\nexport function addOptions(cmd: Command, ...optionGroups: Option[][]): Command {\n for (const group of optionGroups) {\n for (const opt of group) cmd.addOption(opt);\n }\n return cmd;\n}\n\n/** Server address only (for commands with a fixed output format). */\nexport const AddressOptions = (): Option[] => [\n new Option(\"-a, --address <url>\", \"Platforma server address\")\n .env(\"PL_ADDRESS\")\n .makeOptionMandatory(),\n];\n\nexport const GlobalOptions = (\n formatDefault: OutputFormat = \"text\",\n allowedFormats: OutputFormat[] = [\"text\", \"json\"],\n): Option[] => [\n ...AddressOptions(),\n new Option(\"-f, --format <format>\", \"Output format\")\n .choices(allowedFormats)\n .default(formatDefault),\n];\n\nexport const UserAuthOptions = (): Option[] => [\n new Option(\"-u, --user <user>\", \"Username for authentication\").env(\"PL_USER\"),\n new Option(\"-p, --password <password>\", \"Password for authentication\").env(\"PL_PASSWORD\"),\n];\n\n/** Admin credentials only (for purely admin commands like copy-project). */\nexport const AdminAuthOptions = (): Option[] => [\n new Option(\"--admin-user <user>\", \"Admin/controller username\")\n .env(\"PL_ADMIN_USER\")\n .makeOptionMandatory(),\n new Option(\"--admin-password <password>\", \"Admin/controller password\")\n .env(\"PL_ADMIN_PASSWORD\")\n .makeOptionMandatory(),\n];\n\n/** Admin credentials + target user (for regular commands that can optionally operate on another user). */\nexport const AdminTargetOptions = (): Option[] => [\n new Option(\"--admin-user <user>\", \"Admin/controller username (enables admin mode)\").env(\n \"PL_ADMIN_USER\",\n ),\n new Option(\"--admin-password <password>\", \"Admin/controller password\").env(\"PL_ADMIN_PASSWORD\"),\n new Option(\n \"--target-user <user>\",\n \"Operate on this user's data (requires admin credentials)\",\n ).env(\"PL_TARGET_USER\"),\n];\n","import { Command } from \"commander\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { listProjects } from \"../../project_ops\";\nimport { formatTable, formatDate, outputJson, outputText } from \"../../output\";\nimport { resourceIdToString } from \"@milaboratories/pl-client\";\n\nexport default function projectListCommand(): Command {\n const cmd = new Command(\"list\").description(\"List all projects for the authenticated user.\");\n\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n\n cmd.action(async (flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const projects = await listProjects(pl, projectListRid);\n\n if (flags.format === \"json\") {\n outputJson(\n projects.map((p) => ({\n id: p.id,\n rid: resourceIdToString(p.rid),\n label: p.label,\n created: p.created.toISOString(),\n lastModified: p.lastModified.toISOString(),\n })),\n );\n } else {\n if (projects.length === 0) {\n outputText(\"No projects found.\");\n return;\n }\n\n outputText(\n formatTable(\n [\"ID\", \"RID\", \"LABEL\", \"CREATED\", \"LAST MODIFIED\"],\n projects.map((p) => [\n p.id,\n resourceIdToString(p.rid),\n p.label,\n formatDate(p.created),\n formatDate(p.lastModified),\n ]),\n ),\n );\n outputText(`\\n${projects.length} project(s)`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { resolveProject, getProjectInfo } from \"../../project_ops\";\nimport { formatDate, outputJson, outputText } from \"../../output\";\nimport { resourceIdToString } from \"@milaboratories/pl-client\";\n\nexport default function projectInfoCommand(): Command {\n const cmd = new Command(\"info\").description(\"Show detailed information about a project.\");\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { id, rid } = await resolveProject(pl, projectListRid, project);\n const info = await getProjectInfo(pl, id, rid);\n\n if (flags.format === \"json\") {\n outputJson({\n id: info.id,\n rid: resourceIdToString(info.rid),\n label: info.label,\n schemaVersion: info.schemaVersion,\n blockCount: info.blockCount,\n blockIds: info.blockIds,\n created: info.created.toISOString(),\n lastModified: info.lastModified.toISOString(),\n });\n } else {\n outputText(`Project: ${info.label}`);\n outputText(`ID: ${info.id}`);\n outputText(`RID: ${resourceIdToString(info.rid)}`);\n outputText(`Schema: ${info.schemaVersion ?? \"(unknown)\"}`);\n outputText(`Blocks: ${info.blockCount}`);\n if (info.blockIds.length > 0) {\n for (const bid of info.blockIds) {\n outputText(` - ${bid}`);\n }\n }\n outputText(`Created: ${formatDate(info.created)}`);\n outputText(`Last Modified: ${formatDate(info.lastModified)}`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { field, resourceIdToString } from \"@milaboratories/pl-client\";\nimport { ProjectMetaKey } from \"@milaboratories/pl-middle-layer\";\nimport { randomUUID } from \"node:crypto\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport {\n resolveProject,\n deduplicateName,\n duplicateProject,\n getExistingLabelsInTx,\n} from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\n\nexport default function projectDuplicateCommand(): Command {\n const cmd = new Command(\"duplicate\").description(\n \"Duplicate a project within the same user. Auto-renames on collision by default.\",\n );\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n cmd.option(\"-n, --name <name>\", \"Name for the duplicate\");\n cmd.addOption(\n new Option(\"--auto-rename\", \"Auto-rename on collision (default: true)\").default(true),\n );\n cmd.option(\"--no-auto-rename\", \"Fail on name collision instead of auto-renaming\");\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { rid: sourceRid } = await resolveProject(pl, projectListRid, project);\n\n const newId = randomUUID();\n\n const result = await pl.withWriteTx(\"duplicateProject\", async (tx) => {\n const sourceMetaStr = await tx.getKValueString(sourceRid, ProjectMetaKey);\n const sourceMeta = JSON.parse(sourceMetaStr);\n const sourceLabel: string = sourceMeta.label;\n\n const existingLabels = await getExistingLabelsInTx(tx, projectListRid);\n\n // Compute new label\n let newLabel: string;\n if (flags.name) {\n if (!flags.autoRename && existingLabels.includes(flags.name)) {\n throw new Error(`Project name \"${flags.name}\" already exists.`);\n }\n newLabel = existingLabels.includes(flags.name)\n ? deduplicateName(flags.name, existingLabels)\n : flags.name;\n } else {\n newLabel = deduplicateName(sourceLabel, existingLabels);\n }\n\n const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });\n tx.createField(field(projectListRid, newId), \"Dynamic\", newPrj);\n await tx.commit();\n\n const projectResourceId = await newPrj.globalId;\n const projectId = resourceIdToString(projectResourceId);\n return { id: projectId, rid: projectResourceId, label: newLabel };\n });\n\n if (flags.format === \"json\") {\n outputJson({ id: result.id, rid: resourceIdToString(result.rid), label: result.label });\n } else {\n outputText(`Duplicated project as \"${result.label}\" (id: ${result.id})`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { resolveProject, renameProject } from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\nimport { resourceIdToString } from \"@milaboratories/pl-client\";\n\nexport default function projectRenameCommand(): Command {\n const cmd = new Command(\"rename\").description(\"Rename a project.\");\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n cmd.addOption(new Option(\"-n, --name <name>\", \"New name for the project\").makeOptionMandatory());\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { id, rid } = await resolveProject(pl, projectListRid, project);\n\n await renameProject(pl, rid, flags.name);\n\n if (flags.format === \"json\") {\n outputJson({ id, rid: resourceIdToString(rid), label: flags.name });\n } else {\n outputText(`Renamed project to \"${flags.name}\"`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { resolveProject, deleteProject, getProjectInfo } from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\n\nexport default function projectDeleteCommand(): Command {\n const cmd = new Command(\"delete\").description(\n \"Delete a project. This permanently destroys all project data.\",\n );\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n cmd.option(\"--force\", \"Skip confirmation\", false);\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { id, rid, fieldName } = await resolveProject(pl, projectListRid, project);\n\n const info = await getProjectInfo(pl, id, rid);\n\n if (!flags.force) {\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const answer = await new Promise<string>((resolve) => {\n rl.question(\n `Delete project \"${info.label}\" (${info.blockCount} blocks)? [y/N] `,\n resolve,\n );\n });\n if (answer.toLowerCase() !== \"y\") {\n outputText(\"Aborted.\");\n return;\n }\n } finally {\n rl.close();\n }\n }\n\n await deleteProject(pl, projectListRid, fieldName);\n\n if (flags.format === \"json\") {\n outputJson({ deleted: true, id, label: info.label });\n } else {\n outputText(`Deleted project \"${info.label}\"`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { field, resourceIdToString } from \"@milaboratories/pl-client\";\nimport { ProjectMetaKey } from \"@milaboratories/pl-middle-layer\";\nimport { randomUUID } from \"node:crypto\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, GlobalOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport {\n resolveProject,\n deduplicateName,\n duplicateProject,\n getExistingLabelsInTx,\n navigateToUserRoot,\n} from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\n\nexport default function adminCopyProjectCommand(): Command {\n const cmd = new Command(\"copy-project\").description(\n \"Copy a project from one user to another. Requires admin/controller credentials.\",\n );\n\n addOptions(cmd, GlobalOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\n \"--source-user <user>\",\n \"Username of the source project owner\",\n ).makeOptionMandatory(),\n );\n cmd.addOption(\n new Option(\"--source-project <project>\", \"Source project ID or label\").makeOptionMandatory(),\n );\n cmd.option(\n \"--target-user <user>\",\n \"Username of the target user (defaults to source-user for same-user copy)\",\n );\n cmd.option(\"-n, --name <name>\", \"Name for the copied project\");\n cmd.addOption(\n new Option(\"--auto-rename\", \"Auto-rename on collision (default: true)\").default(true),\n );\n cmd.option(\"--no-auto-rename\", \"Fail on name collision instead of auto-renaming\");\n\n cmd.action(async (flags) => {\n const pl = await connectClient(flags);\n try {\n const targetUser = flags.targetUser ?? flags.sourceUser;\n\n const source = await navigateToUserRoot(pl, flags.sourceUser);\n const { rid: sourceRid } = await resolveProject(\n pl,\n source.projectListRid,\n flags.sourceProject,\n );\n\n const target =\n targetUser === flags.sourceUser ? source : await navigateToUserRoot(pl, targetUser);\n\n const newId = randomUUID();\n\n const result = await pl.withWriteTx(\"adminCopyProject\", async (tx) => {\n const sourceMetaStr = await tx.getKValueString(sourceRid, ProjectMetaKey);\n const sourceMeta = JSON.parse(sourceMetaStr);\n const sourceLabel: string = sourceMeta.label;\n\n const existingLabels = await getExistingLabelsInTx(tx, target.projectListRid);\n\n let newLabel: string = flags.name ?? sourceLabel;\n\n if (existingLabels.includes(newLabel)) {\n if (!flags.autoRename) {\n throw new Error(`Project name \"${newLabel}\" already exists for target user.`);\n }\n newLabel = deduplicateName(flags.name ?? sourceLabel, existingLabels);\n }\n\n const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });\n tx.createField(field(target.projectListRid, newId), \"Dynamic\", newPrj);\n await tx.commit();\n\n const prjResourceId = await newPrj.globalId;\n const prjId = resourceIdToString(prjResourceId);\n return { id: prjId, rid: prjResourceId, label: newLabel };\n });\n\n if (flags.format === \"json\") {\n outputJson({\n id: result.id,\n rid: resourceIdToString(result.rid),\n label: result.label,\n sourceUser: flags.sourceUser,\n targetUser,\n });\n } else {\n outputText(\n `Copied project from ${flags.sourceUser} to ${targetUser} as \"${result.label}\" (id: ${result.id}, rid: ${resourceIdToString(result.rid)})`,\n );\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, GlobalOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport { outputJson, outputText, outputCsv } from \"../../output\";\n\nexport default function adminUserListCommand(): Command {\n const cmd = new Command(\"user-list\").description(\n \"List all known users' logins from a live server. Requires admin/controller credentials.\",\n );\n\n addOptions(cmd, GlobalOptions(\"csv\", [\"text\", \"json\", \"csv\"]), AdminAuthOptions());\n\n cmd.action(async (flags) => {\n const pl = await connectClient(flags);\n try {\n const logins = (await pl.listUsers()).map((u) => u.login).sort();\n if (flags.format === \"json\") outputJson(logins);\n else if (flags.format === \"text\") outputText(logins.join(\"\\n\"));\n else\n outputCsv(\n [\"login\"],\n logins.map((l) => [l]),\n );\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, GlobalOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport {\n ensureUserProjectList,\n listProjectIdentities,\n moveProjects,\n openUserRoot,\n} from \"../../project_ops\";\nimport type { MovedProject, ProjectIdentityWithLabel } from \"../../project_ops\";\nimport { formatTable, outputJson, outputText } from \"../../output\";\n\nexport default function adminDeleteUserCommand(): Command {\n const cmd = new Command(\"delete-user\").description(\n \"Delete a user account. Optionally re-homes the user's projects to another user first. \" +\n \"Requires admin/controller credentials.\",\n );\n\n cmd.argument(\"<user>\", \"Username of the account to delete\");\n addOptions(cmd, GlobalOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\n \"--move-projects-to <user>\",\n \"Re-home the deleted user's projects to this user before deleting the account\",\n ),\n );\n cmd.addOption(\n new Option(\n \"--delete-projects\",\n \"Delete the user's projects along with the account, losing their data\",\n ),\n );\n cmd.option(\"--force\", \"Skip confirmation\", false);\n\n cmd.action(async (user: string, flags) => {\n // The two modes are alternatives, not a combination: one keeps the projects, the other\n // destroys them, and a request for both says nothing about which was meant.\n if (flags.moveProjectsTo && flags.deleteProjects) {\n throw new Error(\"--move-projects-to and --delete-projects are mutually exclusive\");\n }\n if (flags.moveProjectsTo === user) {\n throw new Error(\"--move-projects-to must name a different user than the one being deleted\");\n }\n // Caught here as well as server-side, where it is authoritative: the operator gets the reason\n // instead of a failure from whichever lookup happens to run first.\n if (flags.adminUser === user) {\n throw new Error(\n `Refusing to delete \"${user}\" — it is the account these credentials authenticate as.`,\n );\n }\n\n const pl = await connectClient(flags);\n try {\n const source = await openUserRoot(pl, user);\n // No project list means the account never opened the app, so it owns nothing — a state to\n // delete straight through, not to fail on.\n const projects =\n source.projectListRid === undefined\n ? []\n : await listProjectIdentities(pl, source.projectListRid);\n\n // Neither mode given while the account still owns projects: refuse rather than pick. Both\n // defaults are wrong to assume — one silently destroys data, the other silently hands it to\n // someone. An account with no projects has nothing to decide about, so it just proceeds.\n if (projects.length > 0 && !flags.moveProjectsTo && !flags.deleteProjects) {\n throw new Error(\n `User \"${user}\" owns ${projects.length} project(s). Pass --move-projects-to <user> to ` +\n \"re-home them, or --delete-projects to delete them with the account.\",\n );\n }\n\n const confirmed = await confirmDeletion(user, projects, flags);\n if (!confirmed) {\n outputText(\"Aborted.\");\n return;\n }\n\n let moved: MovedProject[] = [];\n if (flags.moveProjectsTo && projects.length > 0) {\n const target = await openUserRoot(pl, flags.moveProjectsTo);\n // Created if the target has none, so re-homing to a user who has never opened the app\n // works rather than failing halfway with the account still present.\n const targetList = await ensureUserProjectList(pl, target.userRoot);\n moved = await moveProjects(pl, source.projectListRid!, targetList);\n }\n\n // deleteUser takes the root and everything still attached to it. The listing, the prompt and\n // the move each ran in an earlier transaction, so re-read: a project attached since then would\n // be destroyed without ever being reviewed.\n const remaining =\n source.projectListRid === undefined\n ? []\n : await listProjectIdentities(pl, source.projectListRid);\n assertProjectsAccountedFor(user, flags, projects, remaining);\n\n const report = await pl.deleteUser(user);\n\n if (flags.format === \"json\") {\n outputJson({\n deleted: true,\n user,\n movedTo: flags.moveProjectsTo ?? null,\n movedProjects: moved,\n deletedProjects: flags.moveProjectsTo ? [] : remaining.map((p) => p.label),\n userRootId: report.userRootId === undefined ? null : report.userRootId.toString(),\n userRootDeleted: report.userRootDeleted,\n revokedGrants: report.revokedGrants,\n removedIdentityIndexEntries: report.removedIdentityIndexEntries,\n });\n } else {\n outputText(renderResult(user, flags.moveProjectsTo, moved, remaining));\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n\n/**\n * Refuses while the root holds a project the operator never agreed to lose — attached after the\n * listing, or left behind by the move.\n *\n * A last read, not a lock: it closes the prompt-sized window, not the round trip that follows.\n */\nfunction assertProjectsAccountedFor(\n user: string,\n flags: { moveProjectsTo?: string },\n approved: ProjectIdentityWithLabel[],\n remaining: ProjectIdentityWithLabel[],\n): void {\n if (flags.moveProjectsTo) {\n if (remaining.length === 0) return;\n throw new Error(\n `User \"${user}\" still owns ${remaining.length} project(s) after the move: ` +\n `${remaining.map((p) => p.label).join(\", \")}. The account was NOT deleted — ` +\n \"the projects were attached while the move ran. Re-run the command to move them too.\",\n );\n }\n\n const approvedIds = new Set(approved.map((p) => p.id));\n const unapproved = remaining.filter((p) => !approvedIds.has(p.id));\n if (unapproved.length === 0) return;\n throw new Error(\n `User \"${user}\" gained ${unapproved.length} project(s) since the list you confirmed: ` +\n `${unapproved.map((p) => p.label).join(\", \")}. The account was NOT deleted — ` +\n \"deleting it now would destroy them unreviewed. Re-run the command to see the current list.\",\n );\n}\n\n/** Asks the operator to confirm, naming the affected projects and their fate. */\nasync function confirmDeletion(\n user: string,\n projects: ProjectIdentityWithLabel[],\n flags: { moveProjectsTo?: string; force?: boolean },\n): Promise<boolean> {\n if (flags.force) return true;\n\n const fate = flags.moveProjectsTo\n ? `${projects.length} project(s) will move to \"${flags.moveProjectsTo}\"`\n : projects.length > 0\n ? `${projects.length} project(s) will be PERMANENTLY DELETED`\n : \"the account owns no projects\";\n\n process.stderr.write(`Delete user \"${user}\"? ${fate}.\\n`);\n if (projects.length > 0) {\n process.stderr.write(projects.map((p) => ` - ${p.label}`).join(\"\\n\") + \"\\n\");\n }\n\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const answer = await new Promise<string>((resolve) => {\n rl.question(\"Proceed? [y/N] \", resolve);\n });\n return answer.toLowerCase() === \"y\";\n } finally {\n rl.close();\n }\n}\n\nfunction renderResult(\n user: string,\n movedTo: string | undefined,\n moved: MovedProject[],\n projects: ProjectIdentityWithLabel[],\n): string {\n const lines: string[] = [];\n\n if (movedTo) {\n if (moved.length > 0) {\n lines.push(`Moved ${moved.length} project(s) from ${user} to ${movedTo}:`);\n lines.push(\n formatTable(\n [\"id\", \"name\", \"name in target\"],\n moved.map((p) => [p.id, p.sourceLabel, p.targetLabel]),\n ),\n );\n } else {\n lines.push(`User ${user} had no projects to move.`);\n }\n } else if (projects.length > 0) {\n lines.push(`Deleted ${projects.length} project(s) belonging to ${user}:`);\n lines.push(projects.map((p) => ` - ${p.label}`).join(\"\\n\"));\n }\n\n lines.push(`Deleted user \"${user}\".`);\n return lines.join(\"\\n\");\n}\n","import {\n asSignedResourceId,\n createSignedResourceId,\n isLocalResourceId,\n resourceIdFromString,\n type SignedResourceId,\n} from \"@milaboratories/pl-client\";\n\n/**\n * Admin and controller roles do not need resource signatures: an ID without a signature\n * is accepted by the backend for these roles.\n */\nexport function parseResourceId(raw: string): SignedResourceId {\n const str = raw.trim();\n if (str.includes(\"|\")) return asSignedResourceId(str);\n\n let id: bigint | undefined = resourceIdFromString(str);\n if (id === undefined && /^\\d+$/.test(str)) id = BigInt(str);\n if (id === undefined || id === 0n || isLocalResourceId(id))\n throw new Error(`not a global resource ID: ${raw}`);\n\n return createSignedResourceId(id);\n}\n","import { once } from \"node:events\";\nimport { Command, InvalidArgumentError, Option } from \"commander\";\nimport {\n isNotNullSignedResourceId,\n resourceIdToString,\n resourceType,\n resourceTypeToString,\n type FieldData,\n type KeyValue,\n type OptionalSignedResourceId,\n type ResourceData,\n type ResourceType,\n} from \"@milaboratories/pl-client\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, AddressOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport { parseResourceId } from \"../../resource_id\";\n\nexport default function maintenanceListResourcesCommand(): Command {\n const cmd = new Command(\"list-resources\").description(\n \"List resources of one type as JSON lines, in ascending order of resource ID. \" +\n \"Resource data and key-value values are printed as JSON when they are valid JSON, \" +\n 'as text when they are valid UTF-8, and as {\"base64\": ...} in other cases. ' +\n \"Requires admin/controller credentials.\",\n );\n\n addOptions(cmd, AddressOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\"--type <name:version>\", \"Resource type to list (e.g. 'Blob/work:1')\")\n .argParser(parseResourceTypeArg)\n .makeOptionMandatory(),\n );\n cmd.addOption(\n new Option(\"--limit <n>\", \"Maximum number of resources to list. 0 lists all resources\")\n .argParser(parseNonNegativeInt)\n .default(0),\n );\n cmd.addOption(\n new Option(\n \"--after <resource-id>\",\n \"List only resources with IDs greater than this ID. \" +\n \"To get the next page, give the ID of the last resource of the previous page\",\n ).argParser(parseResourceId),\n );\n cmd.option(\n \"--load-fields\",\n \"Load the fields and the error of each resource ('fields' and 'error' keys)\",\n false,\n );\n cmd.option(\"--load-kv\", \"Load the key-value entries of each resource ('kv' key)\", false);\n\n cmd.action(async (flags) => {\n const pl = await connectClient(flags);\n try {\n const resources = await pl.withReadTx(\"listResources\", async (tx) => {\n const list = await tx.listResourcesByType(flags.type, {\n limit: flags.limit,\n after: flags.after,\n loadFields: flags.loadFields,\n });\n return await Promise.all(\n list.map(async (resource) => ({\n resource,\n kv: flags.loadKv ? await tx.listKeyValues(resource.id) : undefined,\n })),\n );\n });\n\n for (const { resource, kv } of resources) {\n const line = JSON.stringify(toJson(resource, flags.loadFields, kv)) + \"\\n\";\n // Wait for a slow reader: else the output collects in memory a second time.\n if (!process.stdout.write(line)) await once(process.stdout, \"drain\");\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n\nfunction toJson(\n r: ResourceData,\n withFields: boolean,\n kv: KeyValue[] | undefined,\n): Record<string, unknown> {\n return {\n id: resourceIdToString(r.id),\n type: resourceTypeToString(r.type),\n kind: r.kind,\n originalId: optionalIdToString(r.originalResourceId),\n inputsLocked: r.inputsLocked,\n outputsLocked: r.outputsLocked,\n ready: r.resourceReady,\n final: r.final,\n data: r.data === undefined || r.data.length === 0 ? null : decodeBytes(r.data),\n // The backend sends the resource error as a field: without fields it is not known.\n ...(withFields\n ? { error: optionalIdToString(r.error), fields: r.fields.map(fieldToJson) }\n : {}),\n ...(kv === undefined\n ? {}\n : { kv: Object.fromEntries(kv.map((e) => [e.key, decodeBytes(e.value)])) }),\n };\n}\n\nfunction fieldToJson(f: FieldData): Record<string, unknown> {\n return {\n name: f.name,\n type: f.type,\n status: f.status,\n value: optionalIdToString(f.value),\n error: optionalIdToString(f.error),\n valueIsFinal: f.valueIsFinal,\n };\n}\n\nfunction optionalIdToString(id: OptionalSignedResourceId): string | null {\n return isNotNullSignedResourceId(id) ? resourceIdToString(id) : null;\n}\n\n/** JSON value when the bytes are valid JSON, text when valid UTF-8, {\"base64\": ...} otherwise. */\nfunction decodeBytes(bytes: Uint8Array): unknown {\n let text: string;\n try {\n text = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n return { base64: Buffer.from(bytes).toString(\"base64\") };\n }\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction parseResourceTypeArg(value: string): ResourceType {\n const sep = value.lastIndexOf(\":\");\n if (sep <= 0 || sep === value.length - 1)\n throw new InvalidArgumentError(`expected <name>:<version>, e.g. 'Blob/work:1': ${value}`);\n return resourceType(value.slice(0, sep), value.slice(sep + 1));\n}\n\nfunction parseNonNegativeInt(value: string): number {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 0)\n throw new InvalidArgumentError(`not a non-negative integer: ${value}`);\n return n;\n}\n","import { Command, Option } from \"commander\";\nimport { isNotNullSignedResourceId, resourceIdToString } from \"@milaboratories/pl-client\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, AddressOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport { outputText } from \"../../output\";\nimport { parseResourceId } from \"../../resource_id\";\n\nconst DefaultErrorMessage =\n \"Manual error set by administrator to trigger blocks re-run. \" +\n \"Run block once again to get rid of it.\";\n\nexport default function maintenanceSetResourceErrorCommand(): Command {\n const cmd = new Command(\"set-resource-error\").description(\n \"Put an error on one or more resources in a single transaction. \" +\n \"Resources that already have an error are skipped. \" +\n \"Requires admin/controller credentials.\",\n );\n\n cmd.argument(\n \"<resource-id...>\",\n \"Resource IDs: as printed by the server (NG:0x1A2B), as decimal numbers, \" +\n \"or as signed IDs (<decimal ID>|<signature hex>)\",\n );\n addOptions(cmd, AddressOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\"-m, --error-message <message>\", \"Error message to put on the resources\").default(\n DefaultErrorMessage,\n ),\n );\n cmd.option(\n \"--skip-missing\",\n \"Skip resources that do not exist instead of failing the whole transaction\",\n false,\n );\n\n cmd.action(async (rawIds: string[], flags) => {\n // Parse all IDs before the connection, so that a typo does not leave a half-done job.\n const ids = rawIds.map(parseResourceId);\n\n const pl = await connectClient(flags);\n try {\n const result = await pl.withWriteTx(\"setResourceError\", async (tx) => {\n const found = await Promise.all(\n ids.map(async (id) => ({\n id,\n data: flags.skipMissing\n ? await tx.getResourceDataIfExists(id, true)\n : await tx.getResourceData(id, true, true),\n })),\n );\n\n const missing = found.filter((r) => r.data === undefined).map((r) => r.id);\n const resources = found.flatMap((r) => (r.data === undefined ? [] : [r.data]));\n const targets = resources.filter((r) => !isNotNullSignedResourceId(r.error));\n const skipped = resources.filter((r) => isNotNullSignedResourceId(r.error));\n\n if (targets.length > 0) {\n // One error resource for all targets: the backend links it to each target separately.\n const error = tx.createError(flags.errorMessage);\n for (const r of targets) tx.setResourceError(r.id, error);\n await tx.commit();\n }\n\n return { targets, skipped, missing };\n });\n\n for (const id of result.missing)\n outputText(`skipped ${resourceIdToString(id)}: resource does not exist`);\n for (const r of result.skipped)\n outputText(`skipped ${resourceIdToString(r.id)}: resource already has an error`);\n for (const r of result.targets) outputText(`error set ${resourceIdToString(r.id)}`);\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport projectListCommand from \"./cmd/project/list\";\nimport projectInfoCommand from \"./cmd/project/info\";\nimport projectDuplicateCommand from \"./cmd/project/duplicate\";\nimport projectRenameCommand from \"./cmd/project/rename\";\nimport projectDeleteCommand from \"./cmd/project/delete\";\nimport adminCopyProjectCommand from \"./cmd/admin/copy-project\";\nimport adminUserListCommand from \"./cmd/admin/user-list\";\nimport adminDeleteUserCommand from \"./cmd/admin/delete-user\";\nimport maintenanceListResourcesCommand from \"./cmd/maintenance/list-resources\";\nimport maintenanceSetResourceErrorCommand from \"./cmd/maintenance/set-resource-error\";\n\nexport function buildProgram(): Command {\n const program = new Command();\n program.name(\"pl-cli\").description(\"Platforma command-line client\");\n\n const project = new Command(\"project\").description(\"Manage projects\");\n project.addCommand(projectListCommand());\n project.addCommand(projectInfoCommand());\n project.addCommand(projectDuplicateCommand());\n project.addCommand(projectRenameCommand());\n project.addCommand(projectDeleteCommand());\n program.addCommand(project);\n\n const admin = new Command(\"admin\").description(\n \"Admin operations (requires controller credentials)\",\n );\n admin.addCommand(adminCopyProjectCommand());\n admin.addCommand(adminUserListCommand());\n admin.addCommand(adminDeleteUserCommand());\n program.addCommand(admin);\n\n const maintenance = new Command(\"maintenance\").description(\n \"Low-level server state repair (requires admin/controller credentials)\",\n );\n maintenance.addCommand(maintenanceListResourcesCommand());\n maintenance.addCommand(maintenanceSetResourceErrorCommand());\n program.addCommand(maintenance);\n\n return program;\n}\n\nexport async function run(argv: string[] = process.argv): Promise<void> {\n await buildProgram().parseAsync(argv);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","names":["openUserRoot","outputText","createAdminPlConnection","duplicateProject","getProjectInfo","getProjectListRid","navigateToUserRoot","moveProjects","outputJson","listProjects","formatTable","deduplicateName","outputCsv","deleteProject","formatDate","getExistingLabelsInTx","renameProject","createPlConnection","resolveProject","connectClient","flags","connect","hasAdminUser","hasAdminPassword","hasTarget","pl","projectListRid","addOptions","optionGroups","group","opt","cmd","AddressOptions","Option","GlobalOptions","allowedFormats","formatDefault","UserAuthOptions","AdminAuthOptions","AdminTargetOptions","projectListCommand","Command","projects","p","resourceIdToString","projectInfoCommand","project","info","id","rid","bid","projectDuplicateCommand","sourceRid","newId","randomUUID","result","sourceMetaStr","tx","ProjectMetaKey","sourceLabel","existingLabels","newLabel","newPrj","field","projectResourceId","projectRenameCommand","projectDeleteCommand","rl","createInterface","resolve","fieldName","adminCopyProjectCommand","targetUser","source","target","prjResourceId","adminUserListCommand","logins","u","l","adminDeleteUserCommand","user","listProjectIdentities","confirmDeletion","moved","targetList","ensureUserProjectList","remaining","report","renderResult","assertProjectsAccountedFor","approvedIds","approved","unapproved","fate","lines","movedTo","parseResourceId","str","raw","asSignedResourceId","resourceIdFromString","isLocalResourceId","createSignedResourceId","maintenanceListResourcesCommand","parseResourceTypeArg","parseNonNegativeInt","resources","list","resource","line","toJson","kv","once","r","resourceTypeToString","optionalIdToString","decodeBytes","withFields","fieldToJson","f","isNotNullSignedResourceId","text","bytes","sep","value","InvalidArgumentError","resourceType","n","DefaultErrorMessage","maintenanceSetResourceErrorCommand","ids","rawIds","found","missing","targets","skipped","error","buildProgram","program","admin","maintenance","run","argv"],"sources":["../src/base_command.ts","../src/cmd-opts.ts","../src/cmd/project/list.ts","../src/cmd/project/info.ts","../src/cmd/project/duplicate.ts","../src/cmd/project/rename.ts","../src/cmd/project/delete.ts","../src/cmd/admin/copy-project.ts","../src/cmd/admin/user-list.ts","../src/cmd/admin/delete-user.ts","../src/resource_id.ts","../src/cmd/maintenance/list-resources.ts","../src/cmd/maintenance/set-resource-error.ts","../src/cli.ts"],"sourcesContent":["import type { PlClient, SignedResourceId } from \"@milaboratories/pl-client\";\nimport { createPlConnection, createAdminPlConnection } from \"./connection\";\nimport { getProjectListRid, navigateToUserRoot } from \"./project_ops\";\n\n/**\n * Low-level: get an authenticated PlClient without resolving a project list.\n * Use this for commands that navigate to multiple users (e.g. admin copy-project).\n *\n * Callers own the returned client and must `await pl.close()` when done.\n */\nexport async function connectClient(flags: {\n address: string;\n user?: string;\n password?: string;\n adminUser?: string;\n adminPassword?: string;\n}): Promise<PlClient> {\n if (flags.adminUser && flags.adminPassword) {\n return createAdminPlConnection({\n address: flags.address,\n adminUser: flags.adminUser,\n adminPassword: flags.adminPassword,\n });\n }\n return createPlConnection({\n address: flags.address,\n user: flags.user,\n password: flags.password,\n });\n}\n\n/**\n * Connect and resolve the project list for a single user.\n * In admin mode (--admin-user + --admin-password + --target-user), operates on the target user's data.\n * In user mode, operates on the authenticated user's own data.\n *\n * Callers own the returned client and must `await pl.close()` when done.\n */\nexport async function connect(flags: {\n address: string;\n user?: string;\n password?: string;\n adminUser?: string;\n adminPassword?: string;\n targetUser?: string;\n}): Promise<{ pl: PlClient; projectListRid: SignedResourceId }> {\n const hasAdminUser = !!flags.adminUser;\n const hasAdminPassword = !!flags.adminPassword;\n const hasTarget = !!flags.targetUser;\n\n // Validate flag combinations\n if (hasTarget && !(hasAdminUser && hasAdminPassword)) {\n throw new Error(\"--target-user requires --admin-user and --admin-password\");\n }\n if ((hasAdminUser || hasAdminPassword) && !hasTarget) {\n throw new Error(\"--admin-user/--admin-password require --target-user for project commands\");\n }\n\n const pl = await connectClient(flags);\n\n let projectListRid: SignedResourceId;\n if (hasTarget) {\n const nav = await navigateToUserRoot(pl, flags.targetUser!);\n projectListRid = nav.projectListRid;\n } else {\n projectListRid = await getProjectListRid(pl);\n }\n\n return { pl, projectListRid };\n}\n","import { Command, Option } from \"commander\";\nimport type { OutputFormat } from \"./output\";\n\n/** Add one or more groups of options to a command, preserving group order. */\nexport function addOptions(cmd: Command, ...optionGroups: Option[][]): Command {\n for (const group of optionGroups) {\n for (const opt of group) cmd.addOption(opt);\n }\n return cmd;\n}\n\n/** Server address only (for commands with a fixed output format). */\nexport const AddressOptions = (): Option[] => [\n new Option(\"-a, --address <url>\", \"Platforma server address\")\n .env(\"PL_ADDRESS\")\n .makeOptionMandatory(),\n];\n\nexport const GlobalOptions = (\n formatDefault: OutputFormat = \"text\",\n allowedFormats: OutputFormat[] = [\"text\", \"json\"],\n): Option[] => [\n ...AddressOptions(),\n new Option(\"-f, --format <format>\", \"Output format\")\n .choices(allowedFormats)\n .default(formatDefault),\n];\n\nexport const UserAuthOptions = (): Option[] => [\n new Option(\"-u, --user <user>\", \"Username for authentication\").env(\"PL_USER\"),\n new Option(\"-p, --password <password>\", \"Password for authentication\").env(\"PL_PASSWORD\"),\n];\n\n/** Admin credentials only (for purely admin commands like copy-project). */\nexport const AdminAuthOptions = (): Option[] => [\n new Option(\"--admin-user <user>\", \"Admin/controller username\")\n .env(\"PL_ADMIN_USER\")\n .makeOptionMandatory(),\n new Option(\"--admin-password <password>\", \"Admin/controller password\")\n .env(\"PL_ADMIN_PASSWORD\")\n .makeOptionMandatory(),\n];\n\n/** Admin credentials + target user (for regular commands that can optionally operate on another user). */\nexport const AdminTargetOptions = (): Option[] => [\n new Option(\"--admin-user <user>\", \"Admin/controller username (enables admin mode)\").env(\n \"PL_ADMIN_USER\",\n ),\n new Option(\"--admin-password <password>\", \"Admin/controller password\").env(\"PL_ADMIN_PASSWORD\"),\n new Option(\n \"--target-user <user>\",\n \"Operate on this user's data (requires admin credentials)\",\n ).env(\"PL_TARGET_USER\"),\n];\n","import { Command } from \"commander\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { listProjects } from \"../../project_ops\";\nimport { formatTable, formatDate, outputJson, outputText } from \"../../output\";\nimport { resourceIdToString } from \"@milaboratories/pl-client\";\n\nexport default function projectListCommand(): Command {\n const cmd = new Command(\"list\").description(\"List all projects for the authenticated user.\");\n\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n\n cmd.action(async (flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const projects = await listProjects(pl, projectListRid);\n\n if (flags.format === \"json\") {\n outputJson(\n projects.map((p) => ({\n id: p.id,\n rid: resourceIdToString(p.rid),\n label: p.label,\n created: p.created.toISOString(),\n lastModified: p.lastModified.toISOString(),\n })),\n );\n } else {\n if (projects.length === 0) {\n outputText(\"No projects found.\");\n return;\n }\n\n outputText(\n formatTable(\n [\"ID\", \"RID\", \"LABEL\", \"CREATED\", \"LAST MODIFIED\"],\n projects.map((p) => [\n p.id,\n resourceIdToString(p.rid),\n p.label,\n formatDate(p.created),\n formatDate(p.lastModified),\n ]),\n ),\n );\n outputText(`\\n${projects.length} project(s)`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { resolveProject, getProjectInfo } from \"../../project_ops\";\nimport { formatDate, outputJson, outputText } from \"../../output\";\nimport { resourceIdToString } from \"@milaboratories/pl-client\";\n\nexport default function projectInfoCommand(): Command {\n const cmd = new Command(\"info\").description(\"Show detailed information about a project.\");\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { id, rid } = await resolveProject(pl, projectListRid, project);\n const info = await getProjectInfo(pl, id, rid);\n\n if (flags.format === \"json\") {\n outputJson({\n id: info.id,\n rid: resourceIdToString(info.rid),\n label: info.label,\n schemaVersion: info.schemaVersion,\n blockCount: info.blockCount,\n blockIds: info.blockIds,\n created: info.created.toISOString(),\n lastModified: info.lastModified.toISOString(),\n });\n } else {\n outputText(`Project: ${info.label}`);\n outputText(`ID: ${info.id}`);\n outputText(`RID: ${resourceIdToString(info.rid)}`);\n outputText(`Schema: ${info.schemaVersion ?? \"(unknown)\"}`);\n outputText(`Blocks: ${info.blockCount}`);\n if (info.blockIds.length > 0) {\n for (const bid of info.blockIds) {\n outputText(` - ${bid}`);\n }\n }\n outputText(`Created: ${formatDate(info.created)}`);\n outputText(`Last Modified: ${formatDate(info.lastModified)}`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { field, resourceIdToString } from \"@milaboratories/pl-client\";\nimport { ProjectMetaKey } from \"@milaboratories/pl-middle-layer\";\nimport { randomUUID } from \"node:crypto\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport {\n resolveProject,\n deduplicateName,\n duplicateProject,\n getExistingLabelsInTx,\n} from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\n\nexport default function projectDuplicateCommand(): Command {\n const cmd = new Command(\"duplicate\").description(\n \"Duplicate a project within the same user. Auto-renames on collision by default.\",\n );\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n cmd.option(\"-n, --name <name>\", \"Name for the duplicate\");\n cmd.addOption(\n new Option(\"--auto-rename\", \"Auto-rename on collision (default: true)\").default(true),\n );\n cmd.option(\"--no-auto-rename\", \"Fail on name collision instead of auto-renaming\");\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { rid: sourceRid } = await resolveProject(pl, projectListRid, project);\n\n const newId = randomUUID();\n\n const result = await pl.withWriteTx(\"duplicateProject\", async (tx) => {\n const sourceMetaStr = await tx.getKValueString(sourceRid, ProjectMetaKey);\n const sourceMeta = JSON.parse(sourceMetaStr);\n const sourceLabel: string = sourceMeta.label;\n\n const existingLabels = await getExistingLabelsInTx(tx, projectListRid);\n\n // Compute new label\n let newLabel: string;\n if (flags.name) {\n if (!flags.autoRename && existingLabels.includes(flags.name)) {\n throw new Error(`Project name \"${flags.name}\" already exists.`);\n }\n newLabel = existingLabels.includes(flags.name)\n ? deduplicateName(flags.name, existingLabels)\n : flags.name;\n } else {\n newLabel = deduplicateName(sourceLabel, existingLabels);\n }\n\n const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });\n tx.createField(field(projectListRid, newId), \"Dynamic\", newPrj);\n await tx.commit();\n\n const projectResourceId = await newPrj.globalId;\n const projectId = resourceIdToString(projectResourceId);\n return { id: projectId, rid: projectResourceId, label: newLabel };\n });\n\n if (flags.format === \"json\") {\n outputJson({ id: result.id, rid: resourceIdToString(result.rid), label: result.label });\n } else {\n outputText(`Duplicated project as \"${result.label}\" (id: ${result.id})`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { resolveProject, renameProject } from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\nimport { resourceIdToString } from \"@milaboratories/pl-client\";\n\nexport default function projectRenameCommand(): Command {\n const cmd = new Command(\"rename\").description(\"Rename a project.\");\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n cmd.addOption(new Option(\"-n, --name <name>\", \"New name for the project\").makeOptionMandatory());\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { id, rid } = await resolveProject(pl, projectListRid, project);\n\n await renameProject(pl, rid, flags.name);\n\n if (flags.format === \"json\") {\n outputJson({ id, rid: resourceIdToString(rid), label: flags.name });\n } else {\n outputText(`Renamed project to \"${flags.name}\"`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { connect } from \"../../base_command\";\nimport { addOptions, GlobalOptions, UserAuthOptions, AdminTargetOptions } from \"../../cmd-opts\";\nimport { resolveProject, deleteProject, getProjectInfo } from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\n\nexport default function projectDeleteCommand(): Command {\n const cmd = new Command(\"delete\").description(\n \"Delete a project. This permanently destroys all project data.\",\n );\n\n cmd.argument(\"<project>\", \"Project ID or label\");\n addOptions(cmd, GlobalOptions(), UserAuthOptions(), AdminTargetOptions());\n cmd.option(\"--force\", \"Skip confirmation\", false);\n\n cmd.action(async (project: string, flags) => {\n const { pl, projectListRid } = await connect(flags);\n try {\n const { id, rid, fieldName } = await resolveProject(pl, projectListRid, project);\n\n const info = await getProjectInfo(pl, id, rid);\n\n if (!flags.force) {\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const answer = await new Promise<string>((resolve) => {\n rl.question(\n `Delete project \"${info.label}\" (${info.blockCount} blocks)? [y/N] `,\n resolve,\n );\n });\n if (answer.toLowerCase() !== \"y\") {\n outputText(\"Aborted.\");\n return;\n }\n } finally {\n rl.close();\n }\n }\n\n await deleteProject(pl, projectListRid, fieldName);\n\n if (flags.format === \"json\") {\n outputJson({ deleted: true, id, label: info.label });\n } else {\n outputText(`Deleted project \"${info.label}\"`);\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { field, resourceIdToString } from \"@milaboratories/pl-client\";\nimport { ProjectMetaKey } from \"@milaboratories/pl-middle-layer\";\nimport { randomUUID } from \"node:crypto\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, GlobalOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport {\n resolveProject,\n deduplicateName,\n duplicateProject,\n getExistingLabelsInTx,\n navigateToUserRoot,\n} from \"../../project_ops\";\nimport { outputJson, outputText } from \"../../output\";\n\nexport default function adminCopyProjectCommand(): Command {\n const cmd = new Command(\"copy-project\").description(\n \"Copy a project from one user to another. Requires admin/controller credentials.\",\n );\n\n addOptions(cmd, GlobalOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\n \"--source-user <user>\",\n \"Username of the source project owner\",\n ).makeOptionMandatory(),\n );\n cmd.addOption(\n new Option(\"--source-project <project>\", \"Source project ID or label\").makeOptionMandatory(),\n );\n cmd.option(\n \"--target-user <user>\",\n \"Username of the target user (defaults to source-user for same-user copy)\",\n );\n cmd.option(\"-n, --name <name>\", \"Name for the copied project\");\n cmd.addOption(\n new Option(\"--auto-rename\", \"Auto-rename on collision (default: true)\").default(true),\n );\n cmd.option(\"--no-auto-rename\", \"Fail on name collision instead of auto-renaming\");\n\n cmd.action(async (flags) => {\n const pl = await connectClient(flags);\n try {\n const targetUser = flags.targetUser ?? flags.sourceUser;\n\n const source = await navigateToUserRoot(pl, flags.sourceUser);\n const { rid: sourceRid } = await resolveProject(\n pl,\n source.projectListRid,\n flags.sourceProject,\n );\n\n const target =\n targetUser === flags.sourceUser ? source : await navigateToUserRoot(pl, targetUser);\n\n const newId = randomUUID();\n\n const result = await pl.withWriteTx(\"adminCopyProject\", async (tx) => {\n const sourceMetaStr = await tx.getKValueString(sourceRid, ProjectMetaKey);\n const sourceMeta = JSON.parse(sourceMetaStr);\n const sourceLabel: string = sourceMeta.label;\n\n const existingLabels = await getExistingLabelsInTx(tx, target.projectListRid);\n\n let newLabel: string = flags.name ?? sourceLabel;\n\n if (existingLabels.includes(newLabel)) {\n if (!flags.autoRename) {\n throw new Error(`Project name \"${newLabel}\" already exists for target user.`);\n }\n newLabel = deduplicateName(flags.name ?? sourceLabel, existingLabels);\n }\n\n const newPrj = await duplicateProject(tx, sourceRid, { label: newLabel });\n tx.createField(field(target.projectListRid, newId), \"Dynamic\", newPrj);\n await tx.commit();\n\n const prjResourceId = await newPrj.globalId;\n const prjId = resourceIdToString(prjResourceId);\n return { id: prjId, rid: prjResourceId, label: newLabel };\n });\n\n if (flags.format === \"json\") {\n outputJson({\n id: result.id,\n rid: resourceIdToString(result.rid),\n label: result.label,\n sourceUser: flags.sourceUser,\n targetUser,\n });\n } else {\n outputText(\n `Copied project from ${flags.sourceUser} to ${targetUser} as \"${result.label}\" (id: ${result.id}, rid: ${resourceIdToString(result.rid)})`,\n );\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, GlobalOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport { outputJson, outputText, outputCsv } from \"../../output\";\n\nexport default function adminUserListCommand(): Command {\n const cmd = new Command(\"user-list\").description(\n \"List all known users' logins from a live server. Requires admin/controller credentials.\",\n );\n\n addOptions(cmd, GlobalOptions(\"csv\", [\"text\", \"json\", \"csv\"]), AdminAuthOptions());\n\n cmd.action(async (flags) => {\n const pl = await connectClient(flags);\n try {\n const logins = (await pl.listUsers()).map((u) => u.login).sort();\n if (flags.format === \"json\") outputJson(logins);\n else if (flags.format === \"text\") outputText(logins.join(\"\\n\"));\n else\n outputCsv(\n [\"login\"],\n logins.map((l) => [l]),\n );\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command, Option } from \"commander\";\nimport { createInterface } from \"node:readline\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, GlobalOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport {\n ensureUserProjectList,\n listProjectIdentities,\n moveProjects,\n openUserRoot,\n} from \"../../project_ops\";\nimport type { MovedProject, ProjectIdentityWithLabel } from \"../../project_ops\";\nimport { formatTable, outputJson, outputText } from \"../../output\";\n\nexport default function adminDeleteUserCommand(): Command {\n const cmd = new Command(\"delete-user\").description(\n \"Delete a user account. Optionally re-homes the user's projects to another user first. \" +\n \"Requires admin/controller credentials.\",\n );\n\n cmd.argument(\"<user>\", \"Username of the account to delete\");\n addOptions(cmd, GlobalOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\n \"--move-projects-to <user>\",\n \"Re-home the deleted user's projects to this user before deleting the account\",\n ),\n );\n cmd.addOption(\n new Option(\n \"--delete-projects\",\n \"Delete the user's projects along with the account, losing their data\",\n ),\n );\n cmd.option(\"--force\", \"Skip confirmation\", false);\n\n cmd.action(async (user: string, flags) => {\n // The two modes are alternatives, not a combination: one keeps the projects, the other\n // destroys them, and a request for both says nothing about which was meant.\n if (flags.moveProjectsTo && flags.deleteProjects) {\n throw new Error(\"--move-projects-to and --delete-projects are mutually exclusive\");\n }\n if (flags.moveProjectsTo === user) {\n throw new Error(\"--move-projects-to must name a different user than the one being deleted\");\n }\n // Caught here as well as server-side, where it is authoritative: the operator gets the reason\n // instead of a failure from whichever lookup happens to run first.\n if (flags.adminUser === user) {\n throw new Error(\n `Refusing to delete \"${user}\" — it is the account these credentials authenticate as.`,\n );\n }\n\n const pl = await connectClient(flags);\n try {\n const source = await openUserRoot(pl, user);\n // No project list means the account never opened the app, so it owns nothing — a state to\n // delete straight through, not to fail on.\n const projects =\n source.projectListRid === undefined\n ? []\n : await listProjectIdentities(pl, source.projectListRid);\n\n // Neither mode given while the account still owns projects: refuse rather than pick. Both\n // defaults are wrong to assume — one silently destroys data, the other silently hands it to\n // someone. An account with no projects has nothing to decide about, so it just proceeds.\n if (projects.length > 0 && !flags.moveProjectsTo && !flags.deleteProjects) {\n throw new Error(\n `User \"${user}\" owns ${projects.length} project(s). Pass --move-projects-to <user> to ` +\n \"re-home them, or --delete-projects to delete them with the account.\",\n );\n }\n\n const confirmed = await confirmDeletion(user, projects, flags);\n if (!confirmed) {\n outputText(\"Aborted.\");\n return;\n }\n\n let moved: MovedProject[] = [];\n if (flags.moveProjectsTo && projects.length > 0) {\n const target = await openUserRoot(pl, flags.moveProjectsTo);\n // Created if the target has none, so re-homing to a user who has never opened the app\n // works rather than failing halfway with the account still present.\n const targetList = await ensureUserProjectList(pl, target.userRoot);\n moved = await moveProjects(pl, source.projectListRid!, targetList);\n }\n\n // deleteUser takes the root and everything still attached to it. The listing, the prompt and\n // the move each ran in an earlier transaction, so re-read: a project attached since then would\n // be destroyed without ever being reviewed.\n const remaining =\n source.projectListRid === undefined\n ? []\n : await listProjectIdentities(pl, source.projectListRid);\n assertProjectsAccountedFor(user, flags, projects, remaining);\n\n const report = await pl.deleteUser(user);\n\n if (flags.format === \"json\") {\n outputJson({\n deleted: true,\n user,\n movedTo: flags.moveProjectsTo ?? null,\n movedProjects: moved,\n deletedProjects: flags.moveProjectsTo ? [] : remaining.map((p) => p.label),\n userRootId: report.userRootId === undefined ? null : report.userRootId.toString(),\n userRootDeleted: report.userRootDeleted,\n revokedGrants: report.revokedGrants,\n removedIdentityIndexEntries: report.removedIdentityIndexEntries,\n });\n } else {\n outputText(renderResult(user, flags.moveProjectsTo, moved, remaining));\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n\n/**\n * Refuses while the root holds a project the operator never agreed to lose — attached after the\n * listing, or left behind by the move.\n *\n * A last read, not a lock: it closes the prompt-sized window, not the round trip that follows.\n */\nfunction assertProjectsAccountedFor(\n user: string,\n flags: { moveProjectsTo?: string },\n approved: ProjectIdentityWithLabel[],\n remaining: ProjectIdentityWithLabel[],\n): void {\n if (flags.moveProjectsTo) {\n if (remaining.length === 0) return;\n throw new Error(\n `User \"${user}\" still owns ${remaining.length} project(s) after the move: ` +\n `${remaining.map((p) => p.label).join(\", \")}. The account was NOT deleted — ` +\n \"the projects were attached while the move ran. Re-run the command to move them too.\",\n );\n }\n\n const approvedIds = new Set(approved.map((p) => p.id));\n const unapproved = remaining.filter((p) => !approvedIds.has(p.id));\n if (unapproved.length === 0) return;\n throw new Error(\n `User \"${user}\" gained ${unapproved.length} project(s) since the list you confirmed: ` +\n `${unapproved.map((p) => p.label).join(\", \")}. The account was NOT deleted — ` +\n \"deleting it now would destroy them unreviewed. Re-run the command to see the current list.\",\n );\n}\n\n/** Asks the operator to confirm, naming the affected projects and their fate. */\nasync function confirmDeletion(\n user: string,\n projects: ProjectIdentityWithLabel[],\n flags: { moveProjectsTo?: string; force?: boolean },\n): Promise<boolean> {\n if (flags.force) return true;\n\n const fate = flags.moveProjectsTo\n ? `${projects.length} project(s) will move to \"${flags.moveProjectsTo}\"`\n : projects.length > 0\n ? `${projects.length} project(s) will be PERMANENTLY DELETED`\n : \"the account owns no projects\";\n\n process.stderr.write(`Delete user \"${user}\"? ${fate}.\\n`);\n if (projects.length > 0) {\n process.stderr.write(projects.map((p) => ` - ${p.label}`).join(\"\\n\") + \"\\n\");\n }\n\n const rl = createInterface({ input: process.stdin, output: process.stderr });\n try {\n const answer = await new Promise<string>((resolve) => {\n rl.question(\"Proceed? [y/N] \", resolve);\n });\n return answer.toLowerCase() === \"y\";\n } finally {\n rl.close();\n }\n}\n\nfunction renderResult(\n user: string,\n movedTo: string | undefined,\n moved: MovedProject[],\n projects: ProjectIdentityWithLabel[],\n): string {\n const lines: string[] = [];\n\n if (movedTo) {\n if (moved.length > 0) {\n lines.push(`Moved ${moved.length} project(s) from ${user} to ${movedTo}:`);\n lines.push(\n formatTable(\n [\"id\", \"name\", \"name in target\"],\n moved.map((p) => [p.id, p.sourceLabel, p.targetLabel]),\n ),\n );\n } else {\n lines.push(`User ${user} had no projects to move.`);\n }\n } else if (projects.length > 0) {\n lines.push(`Deleted ${projects.length} project(s) belonging to ${user}:`);\n lines.push(projects.map((p) => ` - ${p.label}`).join(\"\\n\"));\n }\n\n lines.push(`Deleted user \"${user}\".`);\n return lines.join(\"\\n\");\n}\n","import {\n asSignedResourceId,\n createSignedResourceId,\n isLocalResourceId,\n resourceIdFromString,\n type SignedResourceId,\n} from \"@milaboratories/pl-client\";\n\n/**\n * Admin and controller roles do not need resource signatures: an ID without a signature\n * is accepted by the backend for these roles.\n */\nexport function parseResourceId(raw: string): SignedResourceId {\n const str = raw.trim();\n if (str.includes(\"|\")) return asSignedResourceId(str);\n\n let id: bigint | undefined = resourceIdFromString(str);\n if (id === undefined && /^\\d+$/.test(str)) id = BigInt(str);\n if (id === undefined || id === 0n || isLocalResourceId(id))\n throw new Error(`not a global resource ID: ${raw}`);\n\n return createSignedResourceId(id);\n}\n","import { once } from \"node:events\";\nimport { Command, InvalidArgumentError, Option } from \"commander\";\nimport {\n isNotNullSignedResourceId,\n resourceIdToString,\n resourceType,\n resourceTypeToString,\n type FieldData,\n type KeyValue,\n type OptionalSignedResourceId,\n type ResourceData,\n type ResourceType,\n} from \"@milaboratories/pl-client\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, AddressOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport { parseResourceId } from \"../../resource_id\";\n\nexport default function maintenanceListResourcesCommand(): Command {\n const cmd = new Command(\"list-resources\").description(\n \"List resources of one type as JSON lines, in ascending order of resource ID. \" +\n \"Resource data and key-value values are printed as JSON when they are valid JSON, \" +\n 'as text when they are valid UTF-8, and as {\"base64\": ...} in other cases. ' +\n \"Requires admin/controller credentials.\",\n );\n\n addOptions(cmd, AddressOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\"--type <name:version>\", \"Resource type to list (e.g. 'Blob/work:1')\")\n .argParser(parseResourceTypeArg)\n .makeOptionMandatory(),\n );\n cmd.addOption(\n new Option(\"--limit <n>\", \"Maximum number of resources to list. 0 lists all resources\")\n .argParser(parseNonNegativeInt)\n .default(0),\n );\n cmd.addOption(\n new Option(\n \"--after <resource-id>\",\n \"List only resources with IDs greater than this ID. \" +\n \"To get the next page, give the ID of the last resource of the previous page\",\n ).argParser(parseResourceId),\n );\n cmd.option(\n \"--load-fields\",\n \"Load the fields and the error of each resource ('fields' and 'error' keys)\",\n false,\n );\n cmd.option(\"--load-kv\", \"Load the key-value entries of each resource ('kv' key)\", false);\n\n cmd.action(async (flags) => {\n const pl = await connectClient(flags);\n try {\n const resources = await pl.withReadTx(\"listResources\", async (tx) => {\n const list = await tx.listResourcesByType(flags.type, {\n limit: flags.limit,\n after: flags.after,\n loadFields: flags.loadFields,\n });\n return await Promise.all(\n list.map(async (resource) => ({\n resource,\n kv: flags.loadKv ? await tx.listKeyValues(resource.id) : undefined,\n })),\n );\n });\n\n for (const { resource, kv } of resources) {\n const line = JSON.stringify(toJson(resource, flags.loadFields, kv)) + \"\\n\";\n // Wait for a slow reader: else the output collects in memory a second time.\n if (!process.stdout.write(line)) await once(process.stdout, \"drain\");\n }\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n\nfunction toJson(\n r: ResourceData,\n withFields: boolean,\n kv: KeyValue[] | undefined,\n): Record<string, unknown> {\n return {\n id: resourceIdToString(r.id),\n type: resourceTypeToString(r.type),\n kind: r.kind,\n originalId: optionalIdToString(r.originalResourceId),\n inputsLocked: r.inputsLocked,\n outputsLocked: r.outputsLocked,\n ready: r.resourceReady,\n final: r.final,\n data: r.data === undefined || r.data.length === 0 ? null : decodeBytes(r.data),\n // The backend sends the resource error as a field: without fields it is not known.\n ...(withFields\n ? { error: optionalIdToString(r.error), fields: r.fields.map(fieldToJson) }\n : {}),\n ...(kv === undefined\n ? {}\n : { kv: Object.fromEntries(kv.map((e) => [e.key, decodeBytes(e.value)])) }),\n };\n}\n\nfunction fieldToJson(f: FieldData): Record<string, unknown> {\n return {\n name: f.name,\n type: f.type,\n status: f.status,\n value: optionalIdToString(f.value),\n error: optionalIdToString(f.error),\n valueIsFinal: f.valueIsFinal,\n };\n}\n\nfunction optionalIdToString(id: OptionalSignedResourceId): string | null {\n return isNotNullSignedResourceId(id) ? resourceIdToString(id) : null;\n}\n\n/** JSON value when the bytes are valid JSON, text when valid UTF-8, {\"base64\": ...} otherwise. */\nfunction decodeBytes(bytes: Uint8Array): unknown {\n let text: string;\n try {\n text = new TextDecoder(\"utf-8\", { fatal: true }).decode(bytes);\n } catch {\n return { base64: Buffer.from(bytes).toString(\"base64\") };\n }\n try {\n return JSON.parse(text);\n } catch {\n return text;\n }\n}\n\nfunction parseResourceTypeArg(value: string): ResourceType {\n const sep = value.lastIndexOf(\":\");\n if (sep <= 0 || sep === value.length - 1)\n throw new InvalidArgumentError(`expected <name>:<version>, e.g. 'Blob/work:1': ${value}`);\n return resourceType(value.slice(0, sep), value.slice(sep + 1));\n}\n\nfunction parseNonNegativeInt(value: string): number {\n const n = Number(value);\n if (!Number.isInteger(n) || n < 0)\n throw new InvalidArgumentError(`not a non-negative integer: ${value}`);\n return n;\n}\n","import { Command, Option } from \"commander\";\nimport { isNotNullSignedResourceId, resourceIdToString } from \"@milaboratories/pl-client\";\nimport { connectClient } from \"../../base_command\";\nimport { addOptions, AddressOptions, AdminAuthOptions } from \"../../cmd-opts\";\nimport { outputText } from \"../../output\";\nimport { parseResourceId } from \"../../resource_id\";\n\nconst DefaultErrorMessage =\n \"Manual error set by administrator to trigger blocks re-run. \" +\n \"Run block once again to get rid of it.\";\n\nexport default function maintenanceSetResourceErrorCommand(): Command {\n const cmd = new Command(\"set-resource-error\").description(\n \"Put an error on one or more resources in a single transaction. \" +\n \"Resources that already have an error are skipped. \" +\n \"Requires admin/controller credentials.\",\n );\n\n cmd.argument(\n \"<resource-id...>\",\n \"Resource IDs: as printed by the server (NG:0x1A2B), as decimal numbers, \" +\n \"or as signed IDs (<decimal ID>|<signature hex>)\",\n );\n addOptions(cmd, AddressOptions(), AdminAuthOptions());\n cmd.addOption(\n new Option(\"-m, --error-message <message>\", \"Error message to put on the resources\").default(\n DefaultErrorMessage,\n ),\n );\n cmd.option(\n \"--skip-missing\",\n \"Skip resources that do not exist instead of failing the whole transaction\",\n false,\n );\n\n cmd.action(async (rawIds: string[], flags) => {\n // Parse all IDs before the connection, so that a typo does not leave a half-done job.\n const ids = rawIds.map(parseResourceId);\n\n const pl = await connectClient(flags);\n try {\n const result = await pl.withWriteTx(\"setResourceError\", async (tx) => {\n const found = await Promise.all(\n ids.map(async (id) => ({\n id,\n data: flags.skipMissing\n ? await tx.getResourceDataIfExists(id, true)\n : await tx.getResourceData(id, true, true),\n })),\n );\n\n const missing = found.filter((r) => r.data === undefined).map((r) => r.id);\n const resources = found.flatMap((r) => (r.data === undefined ? [] : [r.data]));\n const targets = resources.filter((r) => !isNotNullSignedResourceId(r.error));\n const skipped = resources.filter((r) => isNotNullSignedResourceId(r.error));\n\n if (targets.length > 0) {\n // One error resource for all targets: the backend links it to each target separately.\n const error = tx.createError(flags.errorMessage);\n for (const r of targets) tx.setResourceError(r.id, error);\n await tx.commit();\n }\n\n return { targets, skipped, missing };\n });\n\n for (const id of result.missing)\n outputText(`skipped ${resourceIdToString(id)}: resource does not exist`);\n for (const r of result.skipped)\n outputText(`skipped ${resourceIdToString(r.id)}: resource already has an error`);\n for (const r of result.targets) outputText(`error set ${resourceIdToString(r.id)}`);\n } finally {\n await pl.close();\n }\n });\n\n return cmd;\n}\n","import { Command } from \"commander\";\nimport projectListCommand from \"./cmd/project/list\";\nimport projectInfoCommand from \"./cmd/project/info\";\nimport projectDuplicateCommand from \"./cmd/project/duplicate\";\nimport projectRenameCommand from \"./cmd/project/rename\";\nimport projectDeleteCommand from \"./cmd/project/delete\";\nimport adminCopyProjectCommand from \"./cmd/admin/copy-project\";\nimport adminUserListCommand from \"./cmd/admin/user-list\";\nimport adminDeleteUserCommand from \"./cmd/admin/delete-user\";\nimport maintenanceListResourcesCommand from \"./cmd/maintenance/list-resources\";\nimport maintenanceSetResourceErrorCommand from \"./cmd/maintenance/set-resource-error\";\n\nexport function buildProgram(): Command {\n const program = new Command();\n program.name(\"pl-cli\").description(\"Platforma command-line client\");\n\n const project = new Command(\"project\").description(\"Manage projects\");\n project.addCommand(projectListCommand());\n project.addCommand(projectInfoCommand());\n project.addCommand(projectDuplicateCommand());\n project.addCommand(projectRenameCommand());\n project.addCommand(projectDeleteCommand());\n program.addCommand(project);\n\n const admin = new Command(\"admin\").description(\n \"Admin operations (requires controller credentials)\",\n );\n admin.addCommand(adminCopyProjectCommand());\n admin.addCommand(adminUserListCommand());\n admin.addCommand(adminDeleteUserCommand());\n program.addCommand(admin);\n\n const maintenance = new Command(\"maintenance\").description(\n \"Low-level server state repair (requires admin/controller credentials)\",\n );\n maintenance.addCommand(maintenanceListResourcesCommand());\n maintenance.addCommand(maintenanceSetResourceErrorCommand());\n program.addCommand(maintenance);\n\n return program;\n}\n\nexport async function run(argv: string[] = process.argv): Promise<void> {\n await buildProgram().parseAsync(argv);\n}\n"],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1,2 +1 @@
1
- import { a as e, b as t, c as n, d as r, f as i, g as a, i as o, m as s, n as c, o as l, s as u, t as d, u as f, v as p, x as m, y as h } from "./output-BAuWMUzX.js";
2
- export { t as createAdminPlConnection, m as createPlConnection, l as deduplicateName, u as deleteProject, n as duplicateProject, d as formatDate, c as formatTable, f as getExistingLabelsInTx, r as getProjectInfo, i as getProjectListRid, s as listProjects, a as navigateToUserRoot, o as outputJson, e as outputText, p as renameProject, h as resolveProject };
1
+ export { b as createAdminPlConnection, x as createPlConnection, o as deduplicateName, s as deleteProject, c as duplicateProject, t as formatDate, n as formatTable, u as getExistingLabelsInTx, d as getProjectInfo, f as getProjectListRid, m as listProjects, g as navigateToUserRoot, i as outputJson, a as outputText, v as renameProject, y as resolveProject } from "./output-D41Wn0wM.js";
@@ -1,7 +1,7 @@
1
1
  import { PlClient as e, UnauthenticatedPlClient as t, field as n, isNotFoundError as r, isNullSignedResourceId as i, plAddressToConfig as a, resourceIdToString as o } from "@milaboratories/pl-client";
2
2
  import { randomUUID as s } from "node:crypto";
3
3
  import { ProjectCreatedTimestamp as c, ProjectLastModifiedTimestamp as l, ProjectMetaKey as u, ProjectStructureKey as d, ProjectsField as f, ProjectsResourceType as p, SchemaVersionKey as m, duplicateProject as h } from "@milaboratories/pl-middle-layer";
4
- //#region src/connection.ts
4
+ // src/connection.ts
5
5
  async function g(n) {
6
6
  let r = a(n.address);
7
7
  n.user !== void 0 && (r.user = n.user), n.password !== void 0 && (r.password = n.password);
@@ -16,8 +16,7 @@ async function _(n) {
16
16
  let r = a(n.address), i = await (await t.build(r)).login(n.adminUser, n.adminPassword);
17
17
  return await e.init(r, { authInformation: i });
18
18
  }
19
- //#endregion
20
- //#region src/project_ops.ts
19
+ // src/project_ops.ts
21
20
  async function v(e, t) {
22
21
  return await e.withReadTx("listProjects", async (e) => {
23
22
  let n = await e.getResourceData(t, !0), r = [];
@@ -203,8 +202,7 @@ async function j(e, t) {
203
202
  };
204
203
  });
205
204
  }
206
- //#endregion
207
- //#region src/output.ts
205
+ // src/output.ts
208
206
  function M(e) {
209
207
  console.log(e);
210
208
  }
@@ -226,7 +224,6 @@ function F(e, t) {
226
224
  function I(e) {
227
225
  return e.getTime() === 0 ? "(unknown)" : e.toISOString().replace("T", " ").replace(/\.\d+Z$/, "Z");
228
226
  }
229
- //#endregion
230
227
  export { k as _, M as a, _ as b, h as c, x as d, O as f, j as g, D as h, N as i, A as l, v as m, F as n, w as o, y as p, P as r, E as s, I as t, C as u, T as v, g as x, S as y };
231
228
 
232
- //# sourceMappingURL=output-BAuWMUzX.js.map
229
+ //# sourceMappingURL=output-D41Wn0wM.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"output-D41Wn0wM.js","names":["createPlConnection","config","plAddressToConfig","opts","unauth","UnauthenticatedPlClient","authInformation","PlClient","createAdminPlConnection","listProjects","pl","data","tx","projectListRid","entries","f","isNullSignedResourceId","metaStr","createdStr","modifiedStr","ProjectMetaKey","ProjectCreatedTimestamp","ProjectLastModifiedTimestamp","meta","projectId","resourceIdToString","b","a","listProjectIdentities","listProjectIdentitiesInTx","projects","getProjectInfo","kvs","rid","metaKV","kv","createdKV","modifiedKV","schemaKV","SchemaVersionKey","structureKV","ProjectStructureKey","schemaVersion","blockIds","structure","group","block","resolveProject","identifier","getExistingLabelsInTx","labels","deduplicateName","candidate","baseName","i","existingLabels","renameProject","projectRid","updated","newLabel","deleteProject","field","fieldName","moveProjects","sourceProjectListRid","takenLabels","targetProjectListRid","moved","project","sourceLabel","targetLabel","randomUUID","getProjectListRid","fieldData","ProjectsField","openUserRoot","userRoot","username","isNotFoundError","projectsField","ensureUserProjectList","list","ProjectsResourceType","navigateToUserRoot","userRootRid","projectsFieldData","outputText","message","outputJson","outputCsv","esc","v","lines","headers","rows","r","formatTable","colWidths","h","sep","w","row","cell","formatDate","d","duplicateProject"],"sources":["../src/connection.ts","../src/project_ops.ts","../src/output.ts"],"sourcesContent":["import {\n PlClient,\n UnauthenticatedPlClient,\n plAddressToConfig,\n type AuthInformation,\n type PlClientConfig,\n} from \"@milaboratories/pl-client\";\n\nexport interface PlConnectionOptions {\n address: string;\n user?: string;\n password?: string;\n}\n\nexport interface AdminConnectionOptions {\n address: string;\n adminUser: string;\n adminPassword: string;\n}\n\n/** Creates an authenticated PlClient from address + credentials. */\nexport async function createPlConnection(opts: PlConnectionOptions): Promise<PlClient> {\n const config: PlClientConfig = plAddressToConfig(opts.address);\n\n if (opts.user !== undefined) config.user = opts.user;\n if (opts.password !== undefined) config.password = opts.password;\n\n const unauth = await UnauthenticatedPlClient.build(config);\n let authInformation: AuthInformation;\n\n if (await unauth.requireAuth()) {\n if (config.user === undefined || config.password === undefined) {\n throw new Error(\n \"Server requires authentication but no credentials provided. \" +\n \"Use --user/--password flags or PL_USER/PL_PASSWORD env vars.\",\n );\n }\n authInformation = await unauth.login(config.user, config.password);\n } else {\n authInformation = {};\n }\n\n return await PlClient.init(config, { authInformation });\n}\n\n/** Creates a PlClient with admin/controller credentials. */\nexport async function createAdminPlConnection(opts: AdminConnectionOptions): Promise<PlClient> {\n const config: PlClientConfig = plAddressToConfig(opts.address);\n\n const unauth = await UnauthenticatedPlClient.build(config);\n const authInformation: AuthInformation = await unauth.login(opts.adminUser, opts.adminPassword);\n\n return await PlClient.init(config, { authInformation });\n}\n","import type { PlClient, SignedResourceId, PlTransaction } from \"@milaboratories/pl-client\";\nimport {\n field,\n isNotFoundError,\n isNullSignedResourceId,\n resourceIdToString,\n} from \"@milaboratories/pl-client\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n ProjectMetaKey,\n ProjectCreatedTimestamp,\n ProjectLastModifiedTimestamp,\n SchemaVersionKey,\n ProjectStructureKey,\n ProjectsField,\n ProjectsResourceType,\n duplicateProject,\n} from \"@milaboratories/pl-middle-layer\";\nimport type { ProjectMeta } from \"@milaboratories/pl-middle-layer\";\n\nexport interface ProjectEntry {\n id: string;\n rid: SignedResourceId;\n label: string;\n created: Date;\n lastModified: Date;\n}\n\nexport interface ProjectIdentity {\n /** Project ID (resourceIdToString of resource ID) */\n id: string;\n /** Signed resource ID for use in transactions */\n rid: SignedResourceId;\n /** UUID field name in the project list resource */\n fieldName: string;\n}\n\nexport interface ProjectInfo extends ProjectEntry {\n schemaVersion: string | undefined;\n blockCount: number;\n blockIds: string[];\n}\n\n/** List all projects from a project list resource. */\nexport async function listProjects(\n pl: PlClient,\n projectListRid: SignedResourceId,\n): Promise<ProjectEntry[]> {\n return await pl.withReadTx(\"listProjects\", async (tx) => {\n const data = await tx.getResourceData(projectListRid, true);\n const entries: ProjectEntry[] = [];\n\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n\n const [metaStr, createdStr, modifiedStr] = await Promise.all([\n tx.getKValueStringIfExists(f.value, ProjectMetaKey),\n tx.getKValueStringIfExists(f.value, ProjectCreatedTimestamp),\n tx.getKValueStringIfExists(f.value, ProjectLastModifiedTimestamp),\n ]);\n\n const meta: ProjectMeta = metaStr ? JSON.parse(metaStr) : { label: \"(unknown)\" };\n\n const projectId = resourceIdToString(f.value);\n entries.push({\n id: projectId,\n rid: f.value,\n label: meta.label,\n created: createdStr ? new Date(Number(createdStr)) : new Date(0),\n lastModified: modifiedStr ? new Date(Number(modifiedStr)) : new Date(0),\n });\n }\n\n entries.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());\n return entries;\n });\n}\n\n/** A project's identity plus its label — what a whole-root operation needs per project. */\nexport interface ProjectIdentityWithLabel extends ProjectIdentity {\n label: string;\n}\n\n/**\n * Lists every project in a project list with the field name holding it, which\n * {@link listProjects} omits. Whole-root operations (moving a user's projects out before the\n * account is deleted) need that field name to detach each project from its old owner.\n */\nexport async function listProjectIdentities(\n pl: PlClient,\n projectListRid: SignedResourceId,\n): Promise<ProjectIdentityWithLabel[]> {\n return await pl.withReadTx(\"listProjectIdentities\", async (tx) => {\n return await listProjectIdentitiesInTx(tx, projectListRid);\n });\n}\n\n/**\n * {@link listProjectIdentities} against a transaction the caller already holds, so a write that\n * acts on the list can read it in the same transaction rather than trusting an earlier snapshot.\n */\nexport async function listProjectIdentitiesInTx(\n tx: PlTransaction,\n projectListRid: SignedResourceId,\n): Promise<ProjectIdentityWithLabel[]> {\n const data = await tx.getResourceData(projectListRid, true);\n const projects: ProjectIdentityWithLabel[] = [];\n\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n\n const metaStr = await tx.getKValueStringIfExists(f.value, ProjectMetaKey);\n const meta: ProjectMeta = metaStr ? JSON.parse(metaStr) : { label: \"(unknown)\" };\n\n projects.push({\n id: resourceIdToString(f.value),\n rid: f.value,\n fieldName: f.name,\n label: meta.label,\n });\n }\n\n return projects;\n}\n\n/** Get detailed info about a project. */\nexport async function getProjectInfo(\n pl: PlClient,\n projectId: string,\n rid: SignedResourceId,\n): Promise<ProjectInfo> {\n return await pl.withReadTx(\"getProjectInfo\", async (tx) => {\n const kvs = await tx.listKeyValuesString(rid);\n\n const metaKV = kvs.find((kv: { key: string }) => kv.key === ProjectMetaKey);\n const createdKV = kvs.find((kv: { key: string }) => kv.key === ProjectCreatedTimestamp);\n const modifiedKV = kvs.find((kv: { key: string }) => kv.key === ProjectLastModifiedTimestamp);\n const schemaKV = kvs.find((kv: { key: string }) => kv.key === SchemaVersionKey);\n const structureKV = kvs.find((kv: { key: string }) => kv.key === ProjectStructureKey);\n\n const meta: ProjectMeta = metaKV ? JSON.parse(metaKV.value) : { label: \"(unknown)\" };\n const schemaVersion = schemaKV ? JSON.parse(schemaKV.value) : undefined;\n\n const blockIds: string[] = [];\n if (structureKV) {\n const structure = JSON.parse(structureKV.value);\n for (const group of structure.groups ?? []) {\n for (const block of group.blocks ?? []) {\n blockIds.push(block.id);\n }\n }\n }\n\n return {\n id: projectId,\n rid: rid,\n label: meta.label,\n created: createdKV ? new Date(Number(createdKV.value)) : new Date(0),\n lastModified: modifiedKV ? new Date(Number(modifiedKV.value)) : new Date(0),\n schemaVersion,\n blockCount: blockIds.length,\n blockIds,\n };\n });\n}\n\n/** Resolve a project identifier (id or label) to its project ID, ResourceId and field name. */\nexport async function resolveProject(\n pl: PlClient,\n projectListRid: SignedResourceId,\n identifier: string,\n): Promise<ProjectIdentity> {\n return await pl.withReadTx(\"resolveProject\", async (tx) => {\n const data = await tx.getResourceData(projectListRid, true);\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n const projectId = resourceIdToString(f.value);\n if (projectId === identifier) {\n return { id: projectId, rid: f.value, fieldName: f.name };\n }\n }\n // Fallback: match by label\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n const metaStr = await tx.getKValueStringIfExists(f.value, ProjectMetaKey);\n if (metaStr) {\n const meta: ProjectMeta = JSON.parse(metaStr);\n if (meta.label === identifier) {\n return { id: resourceIdToString(f.value), rid: f.value, fieldName: f.name };\n }\n }\n }\n\n throw new Error(`Project \"${identifier}\" not found (searched by id and label).`);\n });\n}\n\n/** Read all project labels within an existing transaction. */\nexport async function getExistingLabelsInTx(\n tx: PlTransaction,\n projectListRid: SignedResourceId,\n): Promise<string[]> {\n const data = await tx.getResourceData(projectListRid, true);\n const labels: string[] = [];\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n const metaStr = await tx.getKValueStringIfExists(f.value, ProjectMetaKey);\n if (metaStr) {\n const meta: ProjectMeta = JSON.parse(metaStr);\n labels.push(meta.label);\n }\n }\n return labels;\n}\n\n/** Get all project labels from a project list. */\nexport async function getProjectLabels(\n pl: PlClient,\n projectListRid: SignedResourceId,\n): Promise<string[]> {\n return await pl.withReadTx(\"getProjectLabels\", async (tx) => {\n return getExistingLabelsInTx(tx, projectListRid);\n });\n}\n\n/**\n * Deduplicates a project name against existing labels.\n * \"X\" → \"X (Copy)\" → \"X (Copy 2)\" → ...\n */\nexport function deduplicateName(baseName: string, existingLabels: string[]): string {\n let candidate = `${baseName} (Copy)`;\n let i = 2;\n while (existingLabels.includes(candidate)) {\n candidate = `${baseName} (Copy ${i})`;\n i++;\n }\n return candidate;\n}\n\n/** Rename a project (update its label). */\nexport async function renameProject(\n pl: PlClient,\n projectRid: SignedResourceId,\n newLabel: string,\n): Promise<void> {\n await pl.withWriteTx(\"renameProject\", async (tx) => {\n const metaStr = await tx.getKValueString(projectRid, ProjectMetaKey);\n const meta: ProjectMeta = JSON.parse(metaStr);\n const updated: ProjectMeta = { ...meta, label: newLabel };\n tx.setKValue(projectRid, ProjectMetaKey, JSON.stringify(updated));\n tx.setKValue(projectRid, ProjectLastModifiedTimestamp, String(Date.now()));\n await tx.commit();\n });\n}\n\n/** Delete a project from the project list. */\nexport async function deleteProject(\n pl: PlClient,\n projectListRid: SignedResourceId,\n fieldName: string,\n): Promise<void> {\n await pl.withWriteTx(\"deleteProject\", async (tx) => {\n tx.removeField(field(projectListRid, fieldName));\n await tx.commit();\n });\n}\n\n/** One project moved by {@link moveProjects}, as it ended up in the target root. */\nexport interface MovedProject {\n id: string;\n /** Label the project had in the source root. */\n sourceLabel: string;\n /** Label it carries in the target root — differs when the name collided and was deduplicated. */\n targetLabel: string;\n}\n\n/**\n * Re-attaches projects from one user's project list to another's, keeping the same project\n * resources — no copy is made, so nothing is duplicated and no data is rewritten.\n *\n * This works across roots because the backend permits a reference between differently coloured\n * resources when the caller holds write access to both, which admin credentials do. Names that\n * collide in the target are deduplicated the way {@link duplicateProject} callers deduplicate\n * theirs, so a move never silently shadows a project the target user already has.\n *\n * The whole batch is one transaction: either every project lands in the target root, or none\n * moves and both roots are untouched.\n */\nexport async function moveProjects(\n pl: PlClient,\n sourceProjectListRid: SignedResourceId,\n targetProjectListRid: SignedResourceId,\n): Promise<MovedProject[]> {\n return await pl.withWriteTx(\"moveProjects\", async (tx) => {\n // Read here rather than taken from the caller: a list read before an operator answered a\n // confirmation prompt describes the account as it was minutes ago, and a project attached\n // since then would be left on the source root — where the account deletion that follows\n // destroys it. Moving what this transaction sees narrows that to the transaction itself.\n const projects = await listProjectIdentitiesInTx(tx, sourceProjectListRid);\n if (projects.length === 0) return [];\n\n const takenLabels = await getExistingLabelsInTx(tx, targetProjectListRid);\n const moved: MovedProject[] = [];\n\n for (const project of projects) {\n const metaStr = await tx.getKValueString(project.rid, ProjectMetaKey);\n const meta: ProjectMeta = JSON.parse(metaStr);\n const sourceLabel = meta.label;\n\n let targetLabel = sourceLabel;\n if (takenLabels.includes(targetLabel)) {\n targetLabel = deduplicateName(sourceLabel, takenLabels);\n tx.setKValue(project.rid, ProjectMetaKey, JSON.stringify({ ...meta, label: targetLabel }));\n tx.setKValue(project.rid, ProjectLastModifiedTimestamp, String(Date.now()));\n }\n // Reserved even when the label was free, so two source projects with the same name do not\n // both keep it.\n takenLabels.push(targetLabel);\n\n // Attached to the target before being detached from the source: the project keeps a\n // reference throughout, so it is never momentarily unreferenced.\n tx.createField(field(targetProjectListRid, randomUUID()), \"Dynamic\", project.rid);\n tx.removeField(field(sourceProjectListRid, project.fieldName));\n\n moved.push({ id: project.id, sourceLabel, targetLabel });\n }\n\n await tx.commit();\n return moved;\n });\n}\n\n/** Get the project list ResourceId for the connected user. */\nexport async function getProjectListRid(pl: PlClient): Promise<SignedResourceId> {\n return await pl.withReadTx(\"getProjectList\", async (tx) => {\n const fieldData = await tx.getField({\n resourceId: tx.clientRoot,\n fieldName: ProjectsField,\n });\n if (isNullSignedResourceId(fieldData.value)) {\n throw new Error(\"No project list found for this user.\");\n }\n return fieldData.value;\n });\n}\n\n/**\n * Resolves a user's root and, if they have one, their project list.\n *\n * Unlike {@link navigateToUserRoot} a missing project list is not an error here: a user who has\n * never opened the app has a root and no list, and that account is still a legitimate target for\n * whole-account operations. Callers that need a list to write into use\n * {@link ensureUserProjectList}.\n */\nexport async function openUserRoot(\n pl: PlClient,\n username: string,\n): Promise<{ userRoot: SignedResourceId; projectListRid: SignedResourceId | undefined }> {\n let userRoot: SignedResourceId | undefined;\n try {\n userRoot = await pl.getUserRoot({ login: username });\n } catch (e) {\n // The backend reports an unknown login as NOT_FOUND on the root, which reads as a missing\n // resource rather than a missing user. Name the actual problem.\n if (isNotFoundError(e)) {\n throw new Error(`User \"${username}\" not found on this server.`);\n }\n throw e;\n }\n if (userRoot === undefined) {\n throw new Error(`User \"${username}\" not found on this server (no root resource).`);\n }\n\n return await pl.withReadTx(\"openUserRoot\", async (tx) => {\n const projectsField = await tx.getFieldIfExists(field(userRoot, ProjectsField));\n if (projectsField === undefined || isNullSignedResourceId(projectsField.value)) {\n return { userRoot, projectListRid: undefined };\n }\n return { userRoot, projectListRid: projectsField.value };\n });\n}\n\n/**\n * Returns a user's project list, creating an empty one when the root has none — the same lazy\n * creation the middle layer performs for the signed-in user, but against an impersonated root, so\n * projects can be re-homed to a user who has never opened the app.\n */\nexport async function ensureUserProjectList(\n pl: PlClient,\n userRoot: SignedResourceId,\n): Promise<SignedResourceId> {\n return await pl.withWriteTx(\"ensureUserProjectList\", async (tx) => {\n const projectsField = field(userRoot, ProjectsField);\n tx.createField(projectsField, \"Dynamic\");\n const fieldData = await tx.getField(projectsField);\n if (!isNullSignedResourceId(fieldData.value)) {\n await tx.commit();\n return fieldData.value;\n }\n\n const list = tx.createEphemeral(ProjectsResourceType);\n tx.lock(list);\n tx.setField(projectsField, list);\n await tx.commit();\n return await list.globalId;\n });\n}\n\n/**\n * Navigates to a specific user's project list resource ID.\n * Tries ListUserResources first (new backend), falls back to SHA256 named resource lookup.\n */\nexport async function navigateToUserRoot(\n pl: PlClient,\n username: string,\n): Promise<{ userRoot: SignedResourceId; projectListRid: SignedResourceId }> {\n let userRootRid: SignedResourceId | undefined;\n\n userRootRid = await pl.getUserRoot({ login: username });\n if (userRootRid === undefined) {\n throw new Error(`User \"${username}\" not found on this server (no root resource).`);\n }\n\n // Read the projects field from the resolved user root\n return await pl.withReadTx(\"navigateToUserProjects\", async (tx) => {\n const projectsFieldData = await tx.getField({\n resourceId: userRootRid,\n fieldName: ProjectsField,\n });\n\n if (isNullSignedResourceId(projectsFieldData.value)) {\n throw new Error(`User \"${username}\" has no project list.`);\n }\n\n return { userRoot: userRootRid, projectListRid: projectsFieldData.value };\n });\n}\n\n// Re-export duplicateProject from pl-middle-layer for use in commands\nexport { duplicateProject };\n","export type OutputFormat = \"text\" | \"json\" | \"csv\";\n\n/** Outputs a line of text to stdout. */\nexport function outputText(message: string): void {\n console.log(message);\n}\n\n/** Outputs data as formatted JSON to stdout. */\nexport function outputJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/** Outputs a header row + data rows as RFC-4180-ish CSV to stdout. */\nexport function outputCsv(headers: string[], rows: string[][]): void {\n const esc = (v: string) => (/[\",\\r\\n]/.test(v) ? `\"${v.replace(/\"/g, '\"\"')}\"` : v);\n const lines = [headers, ...rows].map((r) => r.map(esc).join(\",\"));\n console.log(lines.join(\"\\n\"));\n}\n\n/** Formats a table as aligned text columns. */\nexport function formatTable(headers: string[], rows: string[][]): string {\n const colWidths = headers.map((h, i) =>\n Math.max(h.length, ...rows.map((r) => (r[i] ?? \"\").length)),\n );\n\n const sep = colWidths.map((w) => \"-\".repeat(w)).join(\" \");\n const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(\" \");\n const dataLines = rows.map((row) =>\n row.map((cell, i) => (cell ?? \"\").padEnd(colWidths[i])).join(\" \"),\n );\n\n return [headerLine, sep, ...dataLines].join(\"\\n\");\n}\n\n/** Formats a Date as a short human-readable string. */\nexport function formatDate(d: Date): string {\n if (d.getTime() === 0) return \"(unknown)\";\n return d\n .toISOString()\n .replace(\"T\", \" \")\n .replace(/\\.\\d+Z$/, \"Z\");\n}\n"],"mappings":";;;;AAqBA,eAAsBA,EAAmB,GAA8C;CACrF,IAAMC,IAAyBC,EAAkBC,EAAK,OAAO;CAG7D,AADIA,EAAK,SAAS,KAAA,MAAW,EAAO,OAAOA,EAAK,OAC5CA,EAAK,aAAa,KAAA,MAAW,EAAO,WAAWA,EAAK;CAExD,IAAMC,IAAS,MAAMC,EAAwB,MAAMJ,CAAM,GACrDK;CAEJ,IAAI,MAAMF,EAAO,YAAY,GAAG;EAC9B,IAAIH,EAAO,SAAS,KAAA,KAAaA,EAAO,aAAa,KAAA,GACnD,MAAU,MACR,0HAEF;EAEF,IAAkB,MAAMG,EAAO,MAAMH,EAAO,MAAMA,EAAO,QAAQ;CACnE,OACE,IAAkB,CAAC;CAGrB,OAAO,MAAMM,EAAS,KAAKN,GAAQ,EAAE,mBAAgB,CAAC;AACxD;AAGA,eAAsBO,EAAwB,GAAiD;CAC7F,IAAMP,IAAyBC,EAAkBC,EAAK,OAAO,GAGvDG,IAAmC,OAAM,MAD1BD,EAAwB,MAAMJ,CAAM,EAAA,CACH,MAAME,EAAK,WAAWA,EAAK,aAAa;CAE9F,OAAO,MAAMI,EAAS,KAAKN,GAAQ,EAAE,mBAAgB,CAAC;AACxD;;ACTA,eAAsBQ,EACpB,GACA,GACyB;CACzB,OAAO,MAAMC,EAAG,WAAW,gBAAgB,OAAO,MAAO;EACvD,IAAMC,IAAO,MAAMC,EAAG,gBAAgBC,GAAgB,EAAI,GACpDC,IAA0B,CAAC;EAEjC,KAAK,IAAMC,KAAKJ,EAAK,QAAQ;GAC3B,IAAIK,EAAuBD,EAAE,KAAK,GAAG;GAErC,IAAM,CAACE,GAASC,GAAYC,KAAe,MAAM,QAAQ,IAAI;IAC3DP,EAAG,wBAAwBG,EAAE,OAAOK,CAAc;IAClDR,EAAG,wBAAwBG,EAAE,OAAOM,CAAuB;IAC3DT,EAAG,wBAAwBG,EAAE,OAAOO,CAA4B;GAClE,CAAC,GAEKC,IAAoBN,IAAU,KAAK,MAAMA,CAAO,IAAI,EAAE,OAAO,YAAY,GAEzEO,IAAYC,EAAmBV,EAAE,KAAK;GAC5C,EAAQ,KAAK;IACX,IAAIS;IACJ,KAAKT,EAAE;IACP,OAAOQ,EAAK;IACZ,SAASL,IAAa,IAAI,KAAK,OAAOA,CAAU,CAAC,oBAAI,IAAI,KAAK,CAAC;IAC/D,cAAcC,IAAc,IAAI,KAAK,OAAOA,CAAW,CAAC,oBAAI,IAAI,KAAK,CAAC;GACxE,CAAC;EACH;EAGA,OADAL,EAAQ,MAAM,GAAG,MAAMY,EAAE,aAAa,QAAQ,IAAIC,EAAE,aAAa,QAAQ,CAAC,GACnEb;CACT,CAAC;AACH;AAYA,eAAsBc,EACpB,GACA,GACqC;CACrC,OAAO,MAAMlB,EAAG,WAAW,yBAAyB,OAAO,MAClD,MAAMmB,EAA0BjB,GAAIC,CAAc,CAC1D;AACH;AAMA,eAAsBgB,EACpB,GACA,GACqC;CACrC,IAAMlB,IAAO,MAAMC,EAAG,gBAAgBC,GAAgB,EAAI,GACpDiB,IAAuC,CAAC;CAE9C,KAAK,IAAMf,KAAKJ,EAAK,QAAQ;EAC3B,IAAIK,EAAuBD,EAAE,KAAK,GAAG;EAErC,IAAME,IAAU,MAAML,EAAG,wBAAwBG,EAAE,OAAOK,CAAc,GAClEG,IAAoBN,IAAU,KAAK,MAAMA,CAAO,IAAI,EAAE,OAAO,YAAY;EAE/E,EAAS,KAAK;GACZ,IAAIQ,EAAmBV,EAAE,KAAK;GAC9B,KAAKA,EAAE;GACP,WAAWA,EAAE;GACb,OAAOQ,EAAK;EACd,CAAC;CACH;CAEA,OAAOO;AACT;AAGA,eAAsBC,EACpB,GACA,GACA,GACsB;CACtB,OAAO,MAAMrB,EAAG,WAAW,kBAAkB,OAAO,MAAO;EACzD,IAAMsB,IAAM,MAAMpB,EAAG,oBAAoBqB,CAAG,GAEtCC,IAASF,EAAI,MAAM,MAAwBG,EAAG,QAAQf,CAAc,GACpEgB,IAAYJ,EAAI,MAAM,MAAwBG,EAAG,QAAQd,CAAuB,GAChFgB,IAAaL,EAAI,MAAM,MAAwBG,EAAG,QAAQb,CAA4B,GACtFgB,IAAWN,EAAI,MAAM,MAAwBG,EAAG,QAAQI,CAAgB,GACxEC,IAAcR,EAAI,MAAM,MAAwBG,EAAG,QAAQM,CAAmB,GAE9ElB,IAAoBW,IAAS,KAAK,MAAMA,EAAO,KAAK,IAAI,EAAE,OAAO,YAAY,GAC7EQ,IAAgBJ,IAAW,KAAK,MAAMA,EAAS,KAAK,IAAI,KAAA,GAExDK,IAAqB,CAAC;EAC5B,IAAIH,GAAa;GACf,IAAMI,IAAY,KAAK,MAAMJ,EAAY,KAAK;GAC9C,KAAK,IAAMK,KAASD,EAAU,UAAU,CAAC,GACvC,KAAK,IAAME,KAASD,EAAM,UAAU,CAAC,GACnC,EAAS,KAAKC,EAAM,EAAE;EAG5B;EAEA,OAAO;GACL,IAAItB;GACC;GACL,OAAOD,EAAK;GACZ,SAASa,IAAY,IAAI,KAAK,OAAOA,EAAU,KAAK,CAAC,oBAAI,IAAI,KAAK,CAAC;GACnE,cAAcC,IAAa,IAAI,KAAK,OAAOA,EAAW,KAAK,CAAC,oBAAI,IAAI,KAAK,CAAC;GAC1E;GACA,YAAYM,EAAS;GACrB;EACF;CACF,CAAC;AACH;AAGA,eAAsBI,EACpB,GACA,GACA,GAC0B;CAC1B,OAAO,MAAMrC,EAAG,WAAW,kBAAkB,OAAO,MAAO;EACzD,IAAMC,IAAO,MAAMC,EAAG,gBAAgBC,GAAgB,EAAI;EAC1D,KAAK,IAAME,KAAKJ,EAAK,QAAQ;GAC3B,IAAIK,EAAuBD,EAAE,KAAK,GAAG;GACrC,IAAMS,IAAYC,EAAmBV,EAAE,KAAK;GAC5C,IAAIS,MAAcwB,GAChB,OAAO;IAAE,IAAIxB;IAAW,KAAKT,EAAE;IAAO,WAAWA,EAAE;GAAK;EAE5D;EAEA,KAAK,IAAMA,KAAKJ,EAAK,QAAQ;GAC3B,IAAIK,EAAuBD,EAAE,KAAK,GAAG;GACrC,IAAME,IAAU,MAAML,EAAG,wBAAwBG,EAAE,OAAOK,CAAc;GACxE,IAAIH,KACwB,KAAK,MAAMA,CACjC,CAAA,CAAK,UAAU+B,GACjB,OAAO;IAAE,IAAIvB,EAAmBV,EAAE,KAAK;IAAG,KAAKA,EAAE;IAAO,WAAWA,EAAE;GAAK;EAGhF;EAEA,MAAU,MAAM,YAAYiC,EAAW,wCAAwC;CACjF,CAAC;AACH;AAGA,eAAsBC,EACpB,GACA,GACmB;CACnB,IAAMtC,IAAO,MAAMC,EAAG,gBAAgBC,GAAgB,EAAI,GACpDqC,IAAmB,CAAC;CAC1B,KAAK,IAAMnC,KAAKJ,EAAK,QAAQ;EAC3B,IAAIK,EAAuBD,EAAE,KAAK,GAAG;EACrC,IAAME,IAAU,MAAML,EAAG,wBAAwBG,EAAE,OAAOK,CAAc;EACxE,IAAIH,GAAS;GACX,IAAMM,IAAoB,KAAK,MAAMN,CAAO;GAC5C,EAAO,KAAKM,EAAK,KAAK;EACxB;CACF;CACA,OAAO2B;AACT;AAgBA,SAAgBC,EAAgB,GAAkB,GAAkC;CAClF,IAAIC,IAAY,GAAGC,EAAS,UACxBC,IAAI;CACR,OAAOC,EAAe,SAASH,CAAS,IAEtC,AADA,IAAY,GAAGC,EAAS,SAASC,EAAE,IACnC;CAEF,OAAOF;AACT;AAGA,eAAsBI,EACpB,GACA,GACA,GACe;CACf,MAAM9C,EAAG,YAAY,iBAAiB,OAAO,MAAO;EAClD,IAAMO,IAAU,MAAML,EAAG,gBAAgB6C,GAAYrC,CAAc,GAE7DsC,IAAuB;GAAE,GADL,KAAK,MAAMzC,CACH;GAAM,OAAO0C;EAAS;EAGxD,AAFA/C,EAAG,UAAU6C,GAAYrC,GAAgB,KAAK,UAAUsC,CAAO,CAAC,GAChE9C,EAAG,UAAU6C,GAAYnC,GAA8B,OAAO,KAAK,IAAI,CAAC,CAAC,GACzE,MAAMV,EAAG,OAAO;CAClB,CAAC;AACH;AAGA,eAAsBgD,EACpB,GACA,GACA,GACe;CACf,MAAMlD,EAAG,YAAY,iBAAiB,OAAO,MAAO;EAElD,AADAE,EAAG,YAAYiD,EAAMhD,GAAgBiD,CAAS,CAAC,GAC/C,MAAMlD,EAAG,OAAO;CAClB,CAAC;AACH;AAuBA,eAAsBmD,EACpB,GACA,GACA,GACyB;CACzB,OAAO,MAAMrD,EAAG,YAAY,gBAAgB,OAAO,MAAO;EAKxD,IAAMoB,IAAW,MAAMD,EAA0BjB,GAAIoD,CAAoB;EACzE,IAAIlC,EAAS,WAAW,GAAG,OAAO,CAAC;EAEnC,IAAMmC,IAAc,MAAMhB,EAAsBrC,GAAIsD,CAAoB,GAClEC,IAAwB,CAAC;EAE/B,KAAK,IAAMC,KAAWtC,GAAU;GAC9B,IAAMb,IAAU,MAAML,EAAG,gBAAgBwD,EAAQ,KAAKhD,CAAc,GAC9DG,IAAoB,KAAK,MAAMN,CAAO,GACtCoD,IAAc9C,EAAK,OAErB+C,IAAcD;GAelB,AAdIJ,EAAY,SAASK,CAAW,MAClC,IAAcnB,EAAgBkB,GAAaJ,CAAW,GACtDrD,EAAG,UAAUwD,EAAQ,KAAKhD,GAAgB,KAAK,UAAU;IAAE,GAAGG;IAAM,OAAO+C;GAAY,CAAC,CAAC,GACzF1D,EAAG,UAAUwD,EAAQ,KAAK9C,GAA8B,OAAO,KAAK,IAAI,CAAC,CAAC,IAI5E2C,EAAY,KAAKK,CAAW,GAI5B1D,EAAG,YAAYiD,EAAMK,GAAsBK,EAAW,CAAC,GAAG,WAAWH,EAAQ,GAAG,GAChFxD,EAAG,YAAYiD,EAAMG,GAAsBI,EAAQ,SAAS,CAAC,GAE7DD,EAAM,KAAK;IAAE,IAAIC,EAAQ;IAAI;IAAa;GAAY,CAAC;EACzD;EAGA,OADA,MAAMxD,EAAG,OAAO,GACTuD;CACT,CAAC;AACH;AAGA,eAAsBK,EAAkB,GAAyC;CAC/E,OAAO,MAAM9D,EAAG,WAAW,kBAAkB,OAAO,MAAO;EACzD,IAAM+D,IAAY,MAAM7D,EAAG,SAAS;GAClC,YAAYA,EAAG;GACf,WAAW8D;EACb,CAAC;EACD,IAAI1D,EAAuByD,EAAU,KAAK,GACxC,MAAU,MAAM,sCAAsC;EAExD,OAAOA,EAAU;CACnB,CAAC;AACH;AAUA,eAAsBE,EACpB,GACA,GACuF;CACvF,IAAIC;CACJ,IAAI;EACF,IAAW,MAAMlE,EAAG,YAAY,EAAE,OAAOmE,EAAS,CAAC;CACrD,SAAS,GAAG;EAMV,MAHIC,EAAgB,CAAC,IACT,MAAM,SAASD,EAAS,4BAA4B,IAE1D;CACR;CACA,IAAID,MAAa,KAAA,GACf,MAAU,MAAM,SAASC,EAAS,+CAA+C;CAGnF,OAAO,MAAMnE,EAAG,WAAW,gBAAgB,OAAO,MAAO;EACvD,IAAMqE,IAAgB,MAAMnE,EAAG,iBAAiBiD,EAAMe,GAAUF,CAAa,CAAC;EAI9E,OAHIK,MAAkB,KAAA,KAAa/D,EAAuB+D,EAAc,KAAK,IACpE;GAAE;GAAU,gBAAgB,KAAA;EAAU,IAExC;GAAE;GAAU,gBAAgBA,EAAc;EAAM;CACzD,CAAC;AACH;AAOA,eAAsBC,EACpB,GACA,GAC2B;CAC3B,OAAO,MAAMtE,EAAG,YAAY,yBAAyB,OAAO,MAAO;EACjE,IAAMqE,IAAgBlB,EAAMe,GAAUF,CAAa;EACnD,EAAG,YAAYK,GAAe,SAAS;EACvC,IAAMN,IAAY,MAAM7D,EAAG,SAASmE,CAAa;EACjD,IAAI,CAAC/D,EAAuByD,EAAU,KAAK,GAEzC,OADA,MAAM7D,EAAG,OAAO,GACT6D,EAAU;EAGnB,IAAMQ,IAAOrE,EAAG,gBAAgBsE,CAAoB;EAIpD,OAHAtE,EAAG,KAAKqE,CAAI,GACZrE,EAAG,SAASmE,GAAeE,CAAI,GAC/B,MAAMrE,EAAG,OAAO,GACT,MAAMqE,EAAK;CACpB,CAAC;AACH;AAMA,eAAsBE,EACpB,GACA,GAC2E;CAC3E,IAAIC;CAGJ,IADA,IAAc,MAAM1E,EAAG,YAAY,EAAE,OAAOmE,EAAS,CAAC,GAClDO,MAAgB,KAAA,GAClB,MAAU,MAAM,SAASP,EAAS,+CAA+C;CAInF,OAAO,MAAMnE,EAAG,WAAW,0BAA0B,OAAO,MAAO;EACjE,IAAM2E,IAAoB,MAAMzE,EAAG,SAAS;GAC1C,YAAYwE;GACZ,WAAWV;EACb,CAAC;EAED,IAAI1D,EAAuBqE,EAAkB,KAAK,GAChD,MAAU,MAAM,SAASR,EAAS,uBAAuB;EAG3D,OAAO;GAAE,UAAUO;GAAa,gBAAgBC,EAAkB;EAAM;CAC1E,CAAC;AACH;;ACjbA,SAAgBC,EAAW,GAAuB;CAChD,QAAQ,IAAIC,CAAO;AACrB;AAGA,SAAgBC,EAAW,GAAqB;CAC9C,QAAQ,IAAI,KAAK,UAAU7E,GAAM,MAAM,CAAC,CAAC;AAC3C;AAGA,SAAgB8E,EAAU,GAAmB,GAAwB;CACnE,IAAMC,KAAO,MAAe,WAAW,KAAKC,CAAC,IAAI,IAAIA,EAAE,QAAQ,MAAM,MAAI,EAAE,KAAKA,GAC1EC,IAAQ,CAACC,GAAS,GAAGC,CAAI,CAAC,CAAC,KAAK,MAAMC,EAAE,IAAIL,CAAG,CAAC,CAAC,KAAK,GAAG,CAAC;CAChE,QAAQ,IAAIE,EAAM,KAAK,IAAI,CAAC;AAC9B;AAGA,SAAgBI,EAAY,GAAmB,GAA0B;CACvE,IAAMC,IAAYJ,EAAQ,KAAK,GAAG,MAChC,KAAK,IAAIK,EAAE,QAAQ,GAAGJ,EAAK,KAAK,OAAOC,EAAEzC,MAAM,GAAA,CAAI,MAAM,CAAC,CAC5D,GAEM6C,IAAMF,EAAU,KAAK,MAAM,IAAI,OAAOG,CAAC,CAAC,CAAC,CAAC,KAAK,IAAI;CAMzD,OAAO;EALYP,EAAQ,KAAK,GAAG,MAAMK,EAAE,OAAOD,EAAU3C,EAAE,CAAC,CAAC,CAAC,KAAK,IAK9D;EAAY6C;EAAK,GAJPL,EAAK,KAAK,MAC1BO,EAAI,KAAK,GAAM,OAAOC,KAAQ,GAAA,CAAI,OAAOL,EAAU3C,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAGvC;CAAS,CAAC,CAAC,KAAK,IAAI;AAClD;AAGA,SAAgBiD,EAAW,GAAiB;CAE1C,OADIC,EAAE,QAAQ,MAAM,IAAU,cACvBA,EACJ,YAAY,CAAC,CACb,QAAQ,KAAK,GAAG,CAAC,CACjB,QAAQ,WAAW,GAAG;AAC3B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@platforma-sdk/pl-cli",
3
- "version": "0.10.3",
3
+ "version": "0.10.5",
4
4
  "description": "CLI for Platforma server state manipulation",
5
5
  "license": "UNLICENSED",
6
6
  "bin": {
@@ -22,20 +22,20 @@
22
22
  }
23
23
  },
24
24
  "dependencies": {
25
- "commander": "^15.0.0",
26
- "@milaboratories/pl-client": "3.19.1",
27
- "@milaboratories/pl-middle-layer": "1.74.10"
25
+ "@milaboratories/pl-client": "3.19.2",
26
+ "@milaboratories/pl-middle-layer": "1.74.12",
27
+ "commander": "^15.0.0"
28
28
  },
29
29
  "devDependencies": {
30
- "@types/node": "~24.5.2",
30
+ "@milaboratories/build-configs": "2.0.2",
31
+ "@milaboratories/ts-builder": "1.7.3",
32
+ "@milaboratories/ts-configs": "1.4.0",
33
+ "@types/node": "~24.19.2",
31
34
  "rollup-plugin-node-externals": "^8.0.0",
32
35
  "typescript": "7.0.2",
33
- "vite": "^8.0.6",
34
- "vite-plugin-dts": "^5.0.3",
35
- "vitest": "^4.1.3",
36
- "@milaboratories/ts-builder": "1.7.2",
37
- "@milaboratories/build-configs": "2.0.2",
38
- "@milaboratories/ts-configs": "1.4.0"
36
+ "vite": "^8.3.5",
37
+ "vite-plugin-dts": "^5.1.2",
38
+ "vitest": "^4.1.12"
39
39
  },
40
40
  "repository": {
41
41
  "type": "git",
@@ -1 +0,0 @@
1
- {"version":3,"file":"output-BAuWMUzX.js","names":[],"sources":["../src/connection.ts","../src/project_ops.ts","../src/output.ts"],"sourcesContent":["import {\n PlClient,\n UnauthenticatedPlClient,\n plAddressToConfig,\n type AuthInformation,\n type PlClientConfig,\n} from \"@milaboratories/pl-client\";\n\nexport interface PlConnectionOptions {\n address: string;\n user?: string;\n password?: string;\n}\n\nexport interface AdminConnectionOptions {\n address: string;\n adminUser: string;\n adminPassword: string;\n}\n\n/** Creates an authenticated PlClient from address + credentials. */\nexport async function createPlConnection(opts: PlConnectionOptions): Promise<PlClient> {\n const config: PlClientConfig = plAddressToConfig(opts.address);\n\n if (opts.user !== undefined) config.user = opts.user;\n if (opts.password !== undefined) config.password = opts.password;\n\n const unauth = await UnauthenticatedPlClient.build(config);\n let authInformation: AuthInformation;\n\n if (await unauth.requireAuth()) {\n if (config.user === undefined || config.password === undefined) {\n throw new Error(\n \"Server requires authentication but no credentials provided. \" +\n \"Use --user/--password flags or PL_USER/PL_PASSWORD env vars.\",\n );\n }\n authInformation = await unauth.login(config.user, config.password);\n } else {\n authInformation = {};\n }\n\n return await PlClient.init(config, { authInformation });\n}\n\n/** Creates a PlClient with admin/controller credentials. */\nexport async function createAdminPlConnection(opts: AdminConnectionOptions): Promise<PlClient> {\n const config: PlClientConfig = plAddressToConfig(opts.address);\n\n const unauth = await UnauthenticatedPlClient.build(config);\n const authInformation: AuthInformation = await unauth.login(opts.adminUser, opts.adminPassword);\n\n return await PlClient.init(config, { authInformation });\n}\n","import type { PlClient, SignedResourceId, PlTransaction } from \"@milaboratories/pl-client\";\nimport {\n field,\n isNotFoundError,\n isNullSignedResourceId,\n resourceIdToString,\n} from \"@milaboratories/pl-client\";\nimport { randomUUID } from \"node:crypto\";\nimport {\n ProjectMetaKey,\n ProjectCreatedTimestamp,\n ProjectLastModifiedTimestamp,\n SchemaVersionKey,\n ProjectStructureKey,\n ProjectsField,\n ProjectsResourceType,\n duplicateProject,\n} from \"@milaboratories/pl-middle-layer\";\nimport type { ProjectMeta } from \"@milaboratories/pl-middle-layer\";\n\nexport interface ProjectEntry {\n id: string;\n rid: SignedResourceId;\n label: string;\n created: Date;\n lastModified: Date;\n}\n\nexport interface ProjectIdentity {\n /** Project ID (resourceIdToString of resource ID) */\n id: string;\n /** Signed resource ID for use in transactions */\n rid: SignedResourceId;\n /** UUID field name in the project list resource */\n fieldName: string;\n}\n\nexport interface ProjectInfo extends ProjectEntry {\n schemaVersion: string | undefined;\n blockCount: number;\n blockIds: string[];\n}\n\n/** List all projects from a project list resource. */\nexport async function listProjects(\n pl: PlClient,\n projectListRid: SignedResourceId,\n): Promise<ProjectEntry[]> {\n return await pl.withReadTx(\"listProjects\", async (tx) => {\n const data = await tx.getResourceData(projectListRid, true);\n const entries: ProjectEntry[] = [];\n\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n\n const [metaStr, createdStr, modifiedStr] = await Promise.all([\n tx.getKValueStringIfExists(f.value, ProjectMetaKey),\n tx.getKValueStringIfExists(f.value, ProjectCreatedTimestamp),\n tx.getKValueStringIfExists(f.value, ProjectLastModifiedTimestamp),\n ]);\n\n const meta: ProjectMeta = metaStr ? JSON.parse(metaStr) : { label: \"(unknown)\" };\n\n const projectId = resourceIdToString(f.value);\n entries.push({\n id: projectId,\n rid: f.value,\n label: meta.label,\n created: createdStr ? new Date(Number(createdStr)) : new Date(0),\n lastModified: modifiedStr ? new Date(Number(modifiedStr)) : new Date(0),\n });\n }\n\n entries.sort((a, b) => b.lastModified.getTime() - a.lastModified.getTime());\n return entries;\n });\n}\n\n/** A project's identity plus its label — what a whole-root operation needs per project. */\nexport interface ProjectIdentityWithLabel extends ProjectIdentity {\n label: string;\n}\n\n/**\n * Lists every project in a project list with the field name holding it, which\n * {@link listProjects} omits. Whole-root operations (moving a user's projects out before the\n * account is deleted) need that field name to detach each project from its old owner.\n */\nexport async function listProjectIdentities(\n pl: PlClient,\n projectListRid: SignedResourceId,\n): Promise<ProjectIdentityWithLabel[]> {\n return await pl.withReadTx(\"listProjectIdentities\", async (tx) => {\n return await listProjectIdentitiesInTx(tx, projectListRid);\n });\n}\n\n/**\n * {@link listProjectIdentities} against a transaction the caller already holds, so a write that\n * acts on the list can read it in the same transaction rather than trusting an earlier snapshot.\n */\nexport async function listProjectIdentitiesInTx(\n tx: PlTransaction,\n projectListRid: SignedResourceId,\n): Promise<ProjectIdentityWithLabel[]> {\n const data = await tx.getResourceData(projectListRid, true);\n const projects: ProjectIdentityWithLabel[] = [];\n\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n\n const metaStr = await tx.getKValueStringIfExists(f.value, ProjectMetaKey);\n const meta: ProjectMeta = metaStr ? JSON.parse(metaStr) : { label: \"(unknown)\" };\n\n projects.push({\n id: resourceIdToString(f.value),\n rid: f.value,\n fieldName: f.name,\n label: meta.label,\n });\n }\n\n return projects;\n}\n\n/** Get detailed info about a project. */\nexport async function getProjectInfo(\n pl: PlClient,\n projectId: string,\n rid: SignedResourceId,\n): Promise<ProjectInfo> {\n return await pl.withReadTx(\"getProjectInfo\", async (tx) => {\n const kvs = await tx.listKeyValuesString(rid);\n\n const metaKV = kvs.find((kv: { key: string }) => kv.key === ProjectMetaKey);\n const createdKV = kvs.find((kv: { key: string }) => kv.key === ProjectCreatedTimestamp);\n const modifiedKV = kvs.find((kv: { key: string }) => kv.key === ProjectLastModifiedTimestamp);\n const schemaKV = kvs.find((kv: { key: string }) => kv.key === SchemaVersionKey);\n const structureKV = kvs.find((kv: { key: string }) => kv.key === ProjectStructureKey);\n\n const meta: ProjectMeta = metaKV ? JSON.parse(metaKV.value) : { label: \"(unknown)\" };\n const schemaVersion = schemaKV ? JSON.parse(schemaKV.value) : undefined;\n\n const blockIds: string[] = [];\n if (structureKV) {\n const structure = JSON.parse(structureKV.value);\n for (const group of structure.groups ?? []) {\n for (const block of group.blocks ?? []) {\n blockIds.push(block.id);\n }\n }\n }\n\n return {\n id: projectId,\n rid: rid,\n label: meta.label,\n created: createdKV ? new Date(Number(createdKV.value)) : new Date(0),\n lastModified: modifiedKV ? new Date(Number(modifiedKV.value)) : new Date(0),\n schemaVersion,\n blockCount: blockIds.length,\n blockIds,\n };\n });\n}\n\n/** Resolve a project identifier (id or label) to its project ID, ResourceId and field name. */\nexport async function resolveProject(\n pl: PlClient,\n projectListRid: SignedResourceId,\n identifier: string,\n): Promise<ProjectIdentity> {\n return await pl.withReadTx(\"resolveProject\", async (tx) => {\n const data = await tx.getResourceData(projectListRid, true);\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n const projectId = resourceIdToString(f.value);\n if (projectId === identifier) {\n return { id: projectId, rid: f.value, fieldName: f.name };\n }\n }\n // Fallback: match by label\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n const metaStr = await tx.getKValueStringIfExists(f.value, ProjectMetaKey);\n if (metaStr) {\n const meta: ProjectMeta = JSON.parse(metaStr);\n if (meta.label === identifier) {\n return { id: resourceIdToString(f.value), rid: f.value, fieldName: f.name };\n }\n }\n }\n\n throw new Error(`Project \"${identifier}\" not found (searched by id and label).`);\n });\n}\n\n/** Read all project labels within an existing transaction. */\nexport async function getExistingLabelsInTx(\n tx: PlTransaction,\n projectListRid: SignedResourceId,\n): Promise<string[]> {\n const data = await tx.getResourceData(projectListRid, true);\n const labels: string[] = [];\n for (const f of data.fields) {\n if (isNullSignedResourceId(f.value)) continue;\n const metaStr = await tx.getKValueStringIfExists(f.value, ProjectMetaKey);\n if (metaStr) {\n const meta: ProjectMeta = JSON.parse(metaStr);\n labels.push(meta.label);\n }\n }\n return labels;\n}\n\n/** Get all project labels from a project list. */\nexport async function getProjectLabels(\n pl: PlClient,\n projectListRid: SignedResourceId,\n): Promise<string[]> {\n return await pl.withReadTx(\"getProjectLabels\", async (tx) => {\n return getExistingLabelsInTx(tx, projectListRid);\n });\n}\n\n/**\n * Deduplicates a project name against existing labels.\n * \"X\" → \"X (Copy)\" → \"X (Copy 2)\" → ...\n */\nexport function deduplicateName(baseName: string, existingLabels: string[]): string {\n let candidate = `${baseName} (Copy)`;\n let i = 2;\n while (existingLabels.includes(candidate)) {\n candidate = `${baseName} (Copy ${i})`;\n i++;\n }\n return candidate;\n}\n\n/** Rename a project (update its label). */\nexport async function renameProject(\n pl: PlClient,\n projectRid: SignedResourceId,\n newLabel: string,\n): Promise<void> {\n await pl.withWriteTx(\"renameProject\", async (tx) => {\n const metaStr = await tx.getKValueString(projectRid, ProjectMetaKey);\n const meta: ProjectMeta = JSON.parse(metaStr);\n const updated: ProjectMeta = { ...meta, label: newLabel };\n tx.setKValue(projectRid, ProjectMetaKey, JSON.stringify(updated));\n tx.setKValue(projectRid, ProjectLastModifiedTimestamp, String(Date.now()));\n await tx.commit();\n });\n}\n\n/** Delete a project from the project list. */\nexport async function deleteProject(\n pl: PlClient,\n projectListRid: SignedResourceId,\n fieldName: string,\n): Promise<void> {\n await pl.withWriteTx(\"deleteProject\", async (tx) => {\n tx.removeField(field(projectListRid, fieldName));\n await tx.commit();\n });\n}\n\n/** One project moved by {@link moveProjects}, as it ended up in the target root. */\nexport interface MovedProject {\n id: string;\n /** Label the project had in the source root. */\n sourceLabel: string;\n /** Label it carries in the target root — differs when the name collided and was deduplicated. */\n targetLabel: string;\n}\n\n/**\n * Re-attaches projects from one user's project list to another's, keeping the same project\n * resources — no copy is made, so nothing is duplicated and no data is rewritten.\n *\n * This works across roots because the backend permits a reference between differently coloured\n * resources when the caller holds write access to both, which admin credentials do. Names that\n * collide in the target are deduplicated the way {@link duplicateProject} callers deduplicate\n * theirs, so a move never silently shadows a project the target user already has.\n *\n * The whole batch is one transaction: either every project lands in the target root, or none\n * moves and both roots are untouched.\n */\nexport async function moveProjects(\n pl: PlClient,\n sourceProjectListRid: SignedResourceId,\n targetProjectListRid: SignedResourceId,\n): Promise<MovedProject[]> {\n return await pl.withWriteTx(\"moveProjects\", async (tx) => {\n // Read here rather than taken from the caller: a list read before an operator answered a\n // confirmation prompt describes the account as it was minutes ago, and a project attached\n // since then would be left on the source root — where the account deletion that follows\n // destroys it. Moving what this transaction sees narrows that to the transaction itself.\n const projects = await listProjectIdentitiesInTx(tx, sourceProjectListRid);\n if (projects.length === 0) return [];\n\n const takenLabels = await getExistingLabelsInTx(tx, targetProjectListRid);\n const moved: MovedProject[] = [];\n\n for (const project of projects) {\n const metaStr = await tx.getKValueString(project.rid, ProjectMetaKey);\n const meta: ProjectMeta = JSON.parse(metaStr);\n const sourceLabel = meta.label;\n\n let targetLabel = sourceLabel;\n if (takenLabels.includes(targetLabel)) {\n targetLabel = deduplicateName(sourceLabel, takenLabels);\n tx.setKValue(project.rid, ProjectMetaKey, JSON.stringify({ ...meta, label: targetLabel }));\n tx.setKValue(project.rid, ProjectLastModifiedTimestamp, String(Date.now()));\n }\n // Reserved even when the label was free, so two source projects with the same name do not\n // both keep it.\n takenLabels.push(targetLabel);\n\n // Attached to the target before being detached from the source: the project keeps a\n // reference throughout, so it is never momentarily unreferenced.\n tx.createField(field(targetProjectListRid, randomUUID()), \"Dynamic\", project.rid);\n tx.removeField(field(sourceProjectListRid, project.fieldName));\n\n moved.push({ id: project.id, sourceLabel, targetLabel });\n }\n\n await tx.commit();\n return moved;\n });\n}\n\n/** Get the project list ResourceId for the connected user. */\nexport async function getProjectListRid(pl: PlClient): Promise<SignedResourceId> {\n return await pl.withReadTx(\"getProjectList\", async (tx) => {\n const fieldData = await tx.getField({\n resourceId: tx.clientRoot,\n fieldName: ProjectsField,\n });\n if (isNullSignedResourceId(fieldData.value)) {\n throw new Error(\"No project list found for this user.\");\n }\n return fieldData.value;\n });\n}\n\n/**\n * Resolves a user's root and, if they have one, their project list.\n *\n * Unlike {@link navigateToUserRoot} a missing project list is not an error here: a user who has\n * never opened the app has a root and no list, and that account is still a legitimate target for\n * whole-account operations. Callers that need a list to write into use\n * {@link ensureUserProjectList}.\n */\nexport async function openUserRoot(\n pl: PlClient,\n username: string,\n): Promise<{ userRoot: SignedResourceId; projectListRid: SignedResourceId | undefined }> {\n let userRoot: SignedResourceId | undefined;\n try {\n userRoot = await pl.getUserRoot({ login: username });\n } catch (e) {\n // The backend reports an unknown login as NOT_FOUND on the root, which reads as a missing\n // resource rather than a missing user. Name the actual problem.\n if (isNotFoundError(e)) {\n throw new Error(`User \"${username}\" not found on this server.`);\n }\n throw e;\n }\n if (userRoot === undefined) {\n throw new Error(`User \"${username}\" not found on this server (no root resource).`);\n }\n\n return await pl.withReadTx(\"openUserRoot\", async (tx) => {\n const projectsField = await tx.getFieldIfExists(field(userRoot, ProjectsField));\n if (projectsField === undefined || isNullSignedResourceId(projectsField.value)) {\n return { userRoot, projectListRid: undefined };\n }\n return { userRoot, projectListRid: projectsField.value };\n });\n}\n\n/**\n * Returns a user's project list, creating an empty one when the root has none — the same lazy\n * creation the middle layer performs for the signed-in user, but against an impersonated root, so\n * projects can be re-homed to a user who has never opened the app.\n */\nexport async function ensureUserProjectList(\n pl: PlClient,\n userRoot: SignedResourceId,\n): Promise<SignedResourceId> {\n return await pl.withWriteTx(\"ensureUserProjectList\", async (tx) => {\n const projectsField = field(userRoot, ProjectsField);\n tx.createField(projectsField, \"Dynamic\");\n const fieldData = await tx.getField(projectsField);\n if (!isNullSignedResourceId(fieldData.value)) {\n await tx.commit();\n return fieldData.value;\n }\n\n const list = tx.createEphemeral(ProjectsResourceType);\n tx.lock(list);\n tx.setField(projectsField, list);\n await tx.commit();\n return await list.globalId;\n });\n}\n\n/**\n * Navigates to a specific user's project list resource ID.\n * Tries ListUserResources first (new backend), falls back to SHA256 named resource lookup.\n */\nexport async function navigateToUserRoot(\n pl: PlClient,\n username: string,\n): Promise<{ userRoot: SignedResourceId; projectListRid: SignedResourceId }> {\n let userRootRid: SignedResourceId | undefined;\n\n userRootRid = await pl.getUserRoot({ login: username });\n if (userRootRid === undefined) {\n throw new Error(`User \"${username}\" not found on this server (no root resource).`);\n }\n\n // Read the projects field from the resolved user root\n return await pl.withReadTx(\"navigateToUserProjects\", async (tx) => {\n const projectsFieldData = await tx.getField({\n resourceId: userRootRid,\n fieldName: ProjectsField,\n });\n\n if (isNullSignedResourceId(projectsFieldData.value)) {\n throw new Error(`User \"${username}\" has no project list.`);\n }\n\n return { userRoot: userRootRid, projectListRid: projectsFieldData.value };\n });\n}\n\n// Re-export duplicateProject from pl-middle-layer for use in commands\nexport { duplicateProject };\n","export type OutputFormat = \"text\" | \"json\" | \"csv\";\n\n/** Outputs a line of text to stdout. */\nexport function outputText(message: string): void {\n console.log(message);\n}\n\n/** Outputs data as formatted JSON to stdout. */\nexport function outputJson(data: unknown): void {\n console.log(JSON.stringify(data, null, 2));\n}\n\n/** Outputs a header row + data rows as RFC-4180-ish CSV to stdout. */\nexport function outputCsv(headers: string[], rows: string[][]): void {\n const esc = (v: string) => (/[\",\\r\\n]/.test(v) ? `\"${v.replace(/\"/g, '\"\"')}\"` : v);\n const lines = [headers, ...rows].map((r) => r.map(esc).join(\",\"));\n console.log(lines.join(\"\\n\"));\n}\n\n/** Formats a table as aligned text columns. */\nexport function formatTable(headers: string[], rows: string[][]): string {\n const colWidths = headers.map((h, i) =>\n Math.max(h.length, ...rows.map((r) => (r[i] ?? \"\").length)),\n );\n\n const sep = colWidths.map((w) => \"-\".repeat(w)).join(\" \");\n const headerLine = headers.map((h, i) => h.padEnd(colWidths[i])).join(\" \");\n const dataLines = rows.map((row) =>\n row.map((cell, i) => (cell ?? \"\").padEnd(colWidths[i])).join(\" \"),\n );\n\n return [headerLine, sep, ...dataLines].join(\"\\n\");\n}\n\n/** Formats a Date as a short human-readable string. */\nexport function formatDate(d: Date): string {\n if (d.getTime() === 0) return \"(unknown)\";\n return d\n .toISOString()\n .replace(\"T\", \" \")\n .replace(/\\.\\d+Z$/, \"Z\");\n}\n"],"mappings":";;;;AAqBA,eAAsB,EAAmB,GAA8C;CACrF,IAAM,IAAyB,EAAkB,EAAK,QAAQ;AAG9D,CADI,EAAK,SAAS,KAAA,MAAW,EAAO,OAAO,EAAK,OAC5C,EAAK,aAAa,KAAA,MAAW,EAAO,WAAW,EAAK;CAExD,IAAM,IAAS,MAAM,EAAwB,MAAM,EAAO,EACtD;AAEJ,KAAI,MAAM,EAAO,aAAa,EAAE;AAC9B,MAAI,EAAO,SAAS,KAAA,KAAa,EAAO,aAAa,KAAA,EACnD,OAAU,MACR,2HAED;AAEH,MAAkB,MAAM,EAAO,MAAM,EAAO,MAAM,EAAO,SAAS;OAElE,KAAkB,EAAE;AAGtB,QAAO,MAAM,EAAS,KAAK,GAAQ,EAAE,oBAAiB,CAAC;;AAIzD,eAAsB,EAAwB,GAAiD;CAC7F,IAAM,IAAyB,EAAkB,EAAK,QAAQ,EAGxD,IAAmC,OAD1B,MAAM,EAAwB,MAAM,EAAO,EACJ,MAAM,EAAK,WAAW,EAAK,cAAc;AAE/F,QAAO,MAAM,EAAS,KAAK,GAAQ,EAAE,oBAAiB,CAAC;;;;ACRzD,eAAsB,EACpB,GACA,GACyB;AACzB,QAAO,MAAM,EAAG,WAAW,gBAAgB,OAAO,MAAO;EACvD,IAAM,IAAO,MAAM,EAAG,gBAAgB,GAAgB,GAAK,EACrD,IAA0B,EAAE;AAElC,OAAK,IAAM,KAAK,EAAK,QAAQ;AAC3B,OAAI,EAAuB,EAAE,MAAM,CAAE;GAErC,IAAM,CAAC,GAAS,GAAY,KAAe,MAAM,QAAQ,IAAI;IAC3D,EAAG,wBAAwB,EAAE,OAAO,EAAe;IACnD,EAAG,wBAAwB,EAAE,OAAO,EAAwB;IAC5D,EAAG,wBAAwB,EAAE,OAAO,EAA6B;IAClE,CAAC,EAEI,IAAoB,IAAU,KAAK,MAAM,EAAQ,GAAG,EAAE,OAAO,aAAa,EAE1E,IAAY,EAAmB,EAAE,MAAM;AAC7C,KAAQ,KAAK;IACX,IAAI;IACJ,KAAK,EAAE;IACP,OAAO,EAAK;IACZ,SAAS,IAAa,IAAI,KAAK,OAAO,EAAW,CAAC,mBAAG,IAAI,KAAK,EAAE;IAChE,cAAc,IAAc,IAAI,KAAK,OAAO,EAAY,CAAC,mBAAG,IAAI,KAAK,EAAE;IACxE,CAAC;;AAIJ,SADA,EAAQ,MAAM,GAAG,MAAM,EAAE,aAAa,SAAS,GAAG,EAAE,aAAa,SAAS,CAAC,EACpE;GACP;;AAaJ,eAAsB,EACpB,GACA,GACqC;AACrC,QAAO,MAAM,EAAG,WAAW,yBAAyB,OAAO,MAClD,MAAM,EAA0B,GAAI,EAAe,CAC1D;;AAOJ,eAAsB,EACpB,GACA,GACqC;CACrC,IAAM,IAAO,MAAM,EAAG,gBAAgB,GAAgB,GAAK,EACrD,IAAuC,EAAE;AAE/C,MAAK,IAAM,KAAK,EAAK,QAAQ;AAC3B,MAAI,EAAuB,EAAE,MAAM,CAAE;EAErC,IAAM,IAAU,MAAM,EAAG,wBAAwB,EAAE,OAAO,EAAe,EACnE,IAAoB,IAAU,KAAK,MAAM,EAAQ,GAAG,EAAE,OAAO,aAAa;AAEhF,IAAS,KAAK;GACZ,IAAI,EAAmB,EAAE,MAAM;GAC/B,KAAK,EAAE;GACP,WAAW,EAAE;GACb,OAAO,EAAK;GACb,CAAC;;AAGJ,QAAO;;AAIT,eAAsB,EACpB,GACA,GACA,GACsB;AACtB,QAAO,MAAM,EAAG,WAAW,kBAAkB,OAAO,MAAO;EACzD,IAAM,IAAM,MAAM,EAAG,oBAAoB,EAAI,EAEvC,IAAS,EAAI,MAAM,MAAwB,EAAG,QAAQ,EAAe,EACrE,IAAY,EAAI,MAAM,MAAwB,EAAG,QAAQ,EAAwB,EACjF,IAAa,EAAI,MAAM,MAAwB,EAAG,QAAQ,EAA6B,EACvF,IAAW,EAAI,MAAM,MAAwB,EAAG,QAAQ,EAAiB,EACzE,IAAc,EAAI,MAAM,MAAwB,EAAG,QAAQ,EAAoB,EAE/E,IAAoB,IAAS,KAAK,MAAM,EAAO,MAAM,GAAG,EAAE,OAAO,aAAa,EAC9E,IAAgB,IAAW,KAAK,MAAM,EAAS,MAAM,GAAG,KAAA,GAExD,IAAqB,EAAE;AAC7B,MAAI,GAAa;GACf,IAAM,IAAY,KAAK,MAAM,EAAY,MAAM;AAC/C,QAAK,IAAM,KAAS,EAAU,UAAU,EAAE,CACxC,MAAK,IAAM,KAAS,EAAM,UAAU,EAAE,CACpC,GAAS,KAAK,EAAM,GAAG;;AAK7B,SAAO;GACL,IAAI;GACC;GACL,OAAO,EAAK;GACZ,SAAS,IAAY,IAAI,KAAK,OAAO,EAAU,MAAM,CAAC,mBAAG,IAAI,KAAK,EAAE;GACpE,cAAc,IAAa,IAAI,KAAK,OAAO,EAAW,MAAM,CAAC,mBAAG,IAAI,KAAK,EAAE;GAC3E;GACA,YAAY,EAAS;GACrB;GACD;GACD;;AAIJ,eAAsB,EACpB,GACA,GACA,GAC0B;AAC1B,QAAO,MAAM,EAAG,WAAW,kBAAkB,OAAO,MAAO;EACzD,IAAM,IAAO,MAAM,EAAG,gBAAgB,GAAgB,GAAK;AAC3D,OAAK,IAAM,KAAK,EAAK,QAAQ;AAC3B,OAAI,EAAuB,EAAE,MAAM,CAAE;GACrC,IAAM,IAAY,EAAmB,EAAE,MAAM;AAC7C,OAAI,MAAc,EAChB,QAAO;IAAE,IAAI;IAAW,KAAK,EAAE;IAAO,WAAW,EAAE;IAAM;;AAI7D,OAAK,IAAM,KAAK,EAAK,QAAQ;AAC3B,OAAI,EAAuB,EAAE,MAAM,CAAE;GACrC,IAAM,IAAU,MAAM,EAAG,wBAAwB,EAAE,OAAO,EAAe;AACzE,OAAI,KACwB,KAAK,MAAM,EAAQ,CACpC,UAAU,EACjB,QAAO;IAAE,IAAI,EAAmB,EAAE,MAAM;IAAE,KAAK,EAAE;IAAO,WAAW,EAAE;IAAM;;AAKjF,QAAU,MAAM,YAAY,EAAW,yCAAyC;GAChF;;AAIJ,eAAsB,EACpB,GACA,GACmB;CACnB,IAAM,IAAO,MAAM,EAAG,gBAAgB,GAAgB,GAAK,EACrD,IAAmB,EAAE;AAC3B,MAAK,IAAM,KAAK,EAAK,QAAQ;AAC3B,MAAI,EAAuB,EAAE,MAAM,CAAE;EACrC,IAAM,IAAU,MAAM,EAAG,wBAAwB,EAAE,OAAO,EAAe;AACzE,MAAI,GAAS;GACX,IAAM,IAAoB,KAAK,MAAM,EAAQ;AAC7C,KAAO,KAAK,EAAK,MAAM;;;AAG3B,QAAO;;AAiBT,SAAgB,EAAgB,GAAkB,GAAkC;CAClF,IAAI,IAAY,GAAG,EAAS,UACxB,IAAI;AACR,QAAO,EAAe,SAAS,EAAU,EAEvC,CADA,IAAY,GAAG,EAAS,SAAS,EAAE,IACnC;AAEF,QAAO;;AAIT,eAAsB,EACpB,GACA,GACA,GACe;AACf,OAAM,EAAG,YAAY,iBAAiB,OAAO,MAAO;EAClD,IAAM,IAAU,MAAM,EAAG,gBAAgB,GAAY,EAAe,EAE9D,IAAuB;GAAE,GADL,KAAK,MAAM,EAAQ;GACL,OAAO;GAAU;AAGzD,EAFA,EAAG,UAAU,GAAY,GAAgB,KAAK,UAAU,EAAQ,CAAC,EACjE,EAAG,UAAU,GAAY,GAA8B,OAAO,KAAK,KAAK,CAAC,CAAC,EAC1E,MAAM,EAAG,QAAQ;GACjB;;AAIJ,eAAsB,EACpB,GACA,GACA,GACe;AACf,OAAM,EAAG,YAAY,iBAAiB,OAAO,MAAO;AAElD,EADA,EAAG,YAAY,EAAM,GAAgB,EAAU,CAAC,EAChD,MAAM,EAAG,QAAQ;GACjB;;AAwBJ,eAAsB,EACpB,GACA,GACA,GACyB;AACzB,QAAO,MAAM,EAAG,YAAY,gBAAgB,OAAO,MAAO;EAKxD,IAAM,IAAW,MAAM,EAA0B,GAAI,EAAqB;AAC1E,MAAI,EAAS,WAAW,EAAG,QAAO,EAAE;EAEpC,IAAM,IAAc,MAAM,EAAsB,GAAI,EAAqB,EACnE,IAAwB,EAAE;AAEhC,OAAK,IAAM,KAAW,GAAU;GAC9B,IAAM,IAAU,MAAM,EAAG,gBAAgB,EAAQ,KAAK,EAAe,EAC/D,IAAoB,KAAK,MAAM,EAAQ,EACvC,IAAc,EAAK,OAErB,IAAc;AAelB,GAdI,EAAY,SAAS,EAAY,KACnC,IAAc,EAAgB,GAAa,EAAY,EACvD,EAAG,UAAU,EAAQ,KAAK,GAAgB,KAAK,UAAU;IAAE,GAAG;IAAM,OAAO;IAAa,CAAC,CAAC,EAC1F,EAAG,UAAU,EAAQ,KAAK,GAA8B,OAAO,KAAK,KAAK,CAAC,CAAC,GAI7E,EAAY,KAAK,EAAY,EAI7B,EAAG,YAAY,EAAM,GAAsB,GAAY,CAAC,EAAE,WAAW,EAAQ,IAAI,EACjF,EAAG,YAAY,EAAM,GAAsB,EAAQ,UAAU,CAAC,EAE9D,EAAM,KAAK;IAAE,IAAI,EAAQ;IAAI;IAAa;IAAa,CAAC;;AAI1D,SADA,MAAM,EAAG,QAAQ,EACV;GACP;;AAIJ,eAAsB,EAAkB,GAAyC;AAC/E,QAAO,MAAM,EAAG,WAAW,kBAAkB,OAAO,MAAO;EACzD,IAAM,IAAY,MAAM,EAAG,SAAS;GAClC,YAAY,EAAG;GACf,WAAW;GACZ,CAAC;AACF,MAAI,EAAuB,EAAU,MAAM,CACzC,OAAU,MAAM,uCAAuC;AAEzD,SAAO,EAAU;GACjB;;AAWJ,eAAsB,EACpB,GACA,GACuF;CACvF,IAAI;AACJ,KAAI;AACF,MAAW,MAAM,EAAG,YAAY,EAAE,OAAO,GAAU,CAAC;UAC7C,GAAG;AAMV,QAHI,EAAgB,EAAE,GACV,MAAM,SAAS,EAAS,6BAA6B,GAE3D;;AAER,KAAI,MAAa,KAAA,EACf,OAAU,MAAM,SAAS,EAAS,gDAAgD;AAGpF,QAAO,MAAM,EAAG,WAAW,gBAAgB,OAAO,MAAO;EACvD,IAAM,IAAgB,MAAM,EAAG,iBAAiB,EAAM,GAAU,EAAc,CAAC;AAI/E,SAHI,MAAkB,KAAA,KAAa,EAAuB,EAAc,MAAM,GACrE;GAAE;GAAU,gBAAgB,KAAA;GAAW,GAEzC;GAAE;GAAU,gBAAgB,EAAc;GAAO;GACxD;;AAQJ,eAAsB,EACpB,GACA,GAC2B;AAC3B,QAAO,MAAM,EAAG,YAAY,yBAAyB,OAAO,MAAO;EACjE,IAAM,IAAgB,EAAM,GAAU,EAAc;AACpD,IAAG,YAAY,GAAe,UAAU;EACxC,IAAM,IAAY,MAAM,EAAG,SAAS,EAAc;AAClD,MAAI,CAAC,EAAuB,EAAU,MAAM,CAE1C,QADA,MAAM,EAAG,QAAQ,EACV,EAAU;EAGnB,IAAM,IAAO,EAAG,gBAAgB,EAAqB;AAIrD,SAHA,EAAG,KAAK,EAAK,EACb,EAAG,SAAS,GAAe,EAAK,EAChC,MAAM,EAAG,QAAQ,EACV,MAAM,EAAK;GAClB;;AAOJ,eAAsB,EACpB,GACA,GAC2E;CAC3E,IAAI;AAGJ,KADA,IAAc,MAAM,EAAG,YAAY,EAAE,OAAO,GAAU,CAAC,EACnD,MAAgB,KAAA,EAClB,OAAU,MAAM,SAAS,EAAS,gDAAgD;AAIpF,QAAO,MAAM,EAAG,WAAW,0BAA0B,OAAO,MAAO;EACjE,IAAM,IAAoB,MAAM,EAAG,SAAS;GAC1C,YAAY;GACZ,WAAW;GACZ,CAAC;AAEF,MAAI,EAAuB,EAAkB,MAAM,CACjD,OAAU,MAAM,SAAS,EAAS,wBAAwB;AAG5D,SAAO;GAAE,UAAU;GAAa,gBAAgB,EAAkB;GAAO;GACzE;;;;AChbJ,SAAgB,EAAW,GAAuB;AAChD,SAAQ,IAAI,EAAQ;;AAItB,SAAgB,EAAW,GAAqB;AAC9C,SAAQ,IAAI,KAAK,UAAU,GAAM,MAAM,EAAE,CAAC;;AAI5C,SAAgB,EAAU,GAAmB,GAAwB;CACnE,IAAM,KAAO,MAAe,WAAW,KAAK,EAAE,GAAG,IAAI,EAAE,QAAQ,MAAM,OAAK,CAAC,KAAK,GAC1E,IAAQ,CAAC,GAAS,GAAG,EAAK,CAAC,KAAK,MAAM,EAAE,IAAI,EAAI,CAAC,KAAK,IAAI,CAAC;AACjE,SAAQ,IAAI,EAAM,KAAK,KAAK,CAAC;;AAI/B,SAAgB,EAAY,GAAmB,GAA0B;CACvE,IAAM,IAAY,EAAQ,KAAK,GAAG,MAChC,KAAK,IAAI,EAAE,QAAQ,GAAG,EAAK,KAAK,OAAO,EAAE,MAAM,IAAI,OAAO,CAAC,CAC5D,EAEK,IAAM,EAAU,KAAK,MAAM,IAAI,OAAO,EAAE,CAAC,CAAC,KAAK,KAAK;AAM1D,QAAO;EALY,EAAQ,KAAK,GAAG,MAAM,EAAE,OAAO,EAAU,GAAG,CAAC,CAAC,KAAK,KAAK;EAKvD;EAAK,GAJP,EAAK,KAAK,MAC1B,EAAI,KAAK,GAAM,OAAO,KAAQ,IAAI,OAAO,EAAU,GAAG,CAAC,CAAC,KAAK,KAAK,CACnE;EAEqC,CAAC,KAAK,KAAK;;AAInD,SAAgB,EAAW,GAAiB;AAE1C,QADI,EAAE,SAAS,KAAK,IAAU,cACvB,EACJ,aAAa,CACb,QAAQ,KAAK,IAAI,CACjB,QAAQ,WAAW,IAAI"}