@undefineds.co/xpod 0.3.56 → 0.3.58

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.
Files changed (57) hide show
  1. package/dist/api/container/index.js +3 -0
  2. package/dist/api/container/index.js.map +1 -1
  3. package/dist/api/container/local.js +53 -13
  4. package/dist/api/container/local.js.map +1 -1
  5. package/dist/api/container/types.d.ts +6 -0
  6. package/dist/api/container/types.js.map +1 -1
  7. package/dist/api/handlers/AdminHandler.d.ts +15 -0
  8. package/dist/api/handlers/AdminHandler.js +128 -7
  9. package/dist/api/handlers/AdminHandler.js.map +1 -1
  10. package/dist/cli/commands/obj.d.ts +45 -0
  11. package/dist/cli/commands/obj.js +309 -33
  12. package/dist/cli/commands/obj.js.map +1 -1
  13. package/dist/components/components.jsonld +1 -0
  14. package/dist/components/context.jsonld +36 -0
  15. package/dist/edge/DdnsManager.d.ts +1 -1
  16. package/dist/edge/DdnsManager.js.map +1 -1
  17. package/dist/edge/reachability/P2PRealnetAcceptance.js +4 -3
  18. package/dist/edge/reachability/P2PRealnetAcceptance.js.map +1 -1
  19. package/dist/edge/reachability/TcpP2PSignalingSession.js +26 -17
  20. package/dist/edge/reachability/TcpP2PSignalingSession.js.map +1 -1
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.js +3 -1
  23. package/dist/index.js.map +1 -1
  24. package/dist/tunnel/NgrokTunnelProvider.d.ts +46 -0
  25. package/dist/tunnel/NgrokTunnelProvider.js +344 -0
  26. package/dist/tunnel/NgrokTunnelProvider.js.map +1 -0
  27. package/dist/tunnel/NgrokTunnelProvider.jsonld +215 -0
  28. package/dist/tunnel/TunnelProvider.d.ts +2 -2
  29. package/dist/tunnel/TunnelProvider.js.map +1 -1
  30. package/dist/tunnel/index.d.ts +1 -0
  31. package/dist/tunnel/index.js +3 -1
  32. package/dist/tunnel/index.js.map +1 -1
  33. package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.d.ts +62 -0
  34. package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.d.ts.map +1 -1
  35. package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.js +197 -0
  36. package/node_modules/@undefineds.co/drizzle-solid/dist/core/pod-database.js.map +1 -1
  37. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.d.ts +62 -0
  38. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.d.ts.map +1 -1
  39. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.js +197 -0
  40. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/core/pod-database.js.map +1 -1
  41. package/node_modules/@undefineds.co/drizzle-solid/dist/esm/pod.d.ts.map +1 -1
  42. package/node_modules/@undefineds.co/drizzle-solid/dist/pod.d.ts.map +1 -1
  43. package/node_modules/@undefineds.co/drizzle-solid/package.json +1 -1
  44. package/package.json +10 -6
  45. package/static/app/assets/{index-AaQ1qxhy.js → index-C1yksyT4.js} +28 -28
  46. package/static/app/assets/inrupt-smoke.js +2 -2
  47. package/static/app/assets/main.css +1 -1
  48. package/static/app/assets/main.js +10 -50
  49. package/static/dashboard/assets/dashboard-BssaVKTn.css +1 -0
  50. package/static/dashboard/assets/dashboard-Ckuadl3C.js +54 -0
  51. package/static/dashboard/dashboard.html +2 -2
  52. package/static/dashboard/reachability.html +221 -0
  53. package/static/dashboard/reachability.svg +7 -0
  54. package/static/dashboard/reachability.webmanifest +18 -0
  55. package/static/dashboard/signal-pod.html +293 -0
  56. package/static/dashboard/assets/dashboard-CTk9cn1-.js +0 -52
  57. package/static/dashboard/assets/dashboard-VQRsvXx9.css +0 -1
@@ -1,16 +1,28 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.objCommand = void 0;
4
+ exports.resolveObjMutationContext = resolveObjMutationContext;
5
+ exports.getObjLocalOutboxPath = getObjLocalOutboxPath;
4
6
  exports.redactDescriptorObject = redactDescriptorObject;
7
+ exports.serializeSchemaDescriptor = serializeSchemaDescriptor;
8
+ exports.listSchemaDescriptors = listSchemaDescriptors;
9
+ exports.describeSchemaDescriptor = describeSchemaDescriptor;
5
10
  exports.buildDescriptorUpsertSparql = buildDescriptorUpsertSparql;
6
11
  exports.buildDescriptorPatchSparql = buildDescriptorPatchSparql;
7
12
  exports.buildDescriptorLinkSparql = buildDescriptorLinkSparql;
8
13
  exports.buildDescriptorDeleteSparql = buildDescriptorDeleteSparql;
9
14
  exports.extractReservedWhereSelectors = extractReservedWhereSelectors;
10
15
  exports.buildDescriptorObjectQuery = buildDescriptorObjectQuery;
16
+ exports.descriptorObjectFromTurtle = descriptorObjectFromTurtle;
17
+ exports.descriptorUpsertPlanItem = descriptorUpsertPlanItem;
18
+ exports.executeRows = executeRows;
19
+ exports.mutationItemsResultCode = mutationItemsResultCode;
11
20
  const fs_1 = require("fs");
21
+ const path_1 = require("path");
22
+ const n3_1 = require("n3");
12
23
  const models_1 = require("@undefineds.co/models");
13
24
  const auth_context_1 = require("../lib/auth-context");
25
+ const credentials_store_1 = require("../lib/credentials-store");
14
26
  const output_1 = require("../lib/output");
15
27
  const resource_1 = require("../lib/resource");
16
28
  const rdf_1 = require("./rdf");
@@ -93,14 +105,28 @@ async function readJsonl(input) {
93
105
  function descriptorLocalName(value) {
94
106
  return value.split(/[\/#]/u).filter(Boolean).pop() ?? value;
95
107
  }
108
+ function normalizeDescriptorAlias(value) {
109
+ return value.toLowerCase().replace(/[^a-z0-9]+/gu, '');
110
+ }
111
+ function descriptorAliases(descriptor) {
112
+ const discovery = descriptor;
113
+ return discovery.aliases?.length ? discovery.aliases : [descriptorLocalName(descriptor.class)];
114
+ }
115
+ function descriptorMatchesRef(descriptor, ref) {
116
+ const normalized = normalizeDescriptorAlias(ref);
117
+ return descriptor.uri === ref ||
118
+ descriptor.class === ref ||
119
+ descriptor.resourceKind.toLowerCase() === ref.trim().toLowerCase() ||
120
+ normalizeDescriptorAlias(descriptorLocalName(descriptor.uri)) === normalized ||
121
+ normalizeDescriptorAlias(descriptorLocalName(descriptor.class)) === normalized ||
122
+ descriptorAliases(descriptor).some((alias) => normalizeDescriptorAlias(alias) === normalized);
123
+ }
96
124
  function resolveDescriptorOrNull(schema) {
97
- const exact = models_1.podSchema.describe({ schemaUri: schema });
125
+ const exact = models_1.podSchema.describe({ schemaUri: schema }) ??
126
+ models_1.podSchema.describe?.({ alias: schema });
98
127
  if (exact)
99
128
  return exact;
100
- const normalized = schema.trim().toLowerCase();
101
- return models_1.podSchema.list().find((descriptor) => descriptor.resourceKind.toLowerCase() === normalized ||
102
- descriptorLocalName(descriptor.uri).toLowerCase() === normalized ||
103
- descriptorLocalName(descriptor.class).toLowerCase() === normalized) ?? null;
129
+ return models_1.podSchema.list().find((descriptor) => descriptorMatchesRef(descriptor, schema)) ?? null;
104
130
  }
105
131
  function resolveDescriptor(schema) {
106
132
  const descriptor = resolveDescriptorOrNull(schema);
@@ -112,6 +138,48 @@ function resolveDescriptor(schema) {
112
138
  function resourceUrlFromPlan(podRoot, plan) {
113
139
  return new URL(plan.resourceUri.replace(/^\/+/, ''), podRoot).toString();
114
140
  }
141
+ async function resolveObjMutationContext(argv, commit) {
142
+ try {
143
+ return await (0, auth_context_1.requireAuthContext)(argv);
144
+ }
145
+ catch (error) {
146
+ const podRoot = (0, auth_context_1.normalizeBaseUrl)(argv.url ?? 'https://pod.local/');
147
+ const outboxPath = getObjLocalOutboxPath();
148
+ return {
149
+ baseUrl: podRoot,
150
+ webId: 'urn:xpod:unauthenticated',
151
+ podRoot,
152
+ baseIri: podRoot,
153
+ accessToken: '',
154
+ credentials: {
155
+ url: podRoot,
156
+ webId: 'urn:xpod:unauthenticated',
157
+ authType: 'client_credentials',
158
+ secrets: {
159
+ clientId: '',
160
+ clientSecret: '',
161
+ },
162
+ },
163
+ ...(commit
164
+ ? {
165
+ pendingOnly: true,
166
+ outboxPath,
167
+ pendingWarning: `No Solid credentials found; mutation was written to local xpod outbox at ${outboxPath} and was not saved to a Pod.`,
168
+ }
169
+ : {
170
+ planningOnly: true,
171
+ planningWarning: 'No Solid credentials found; this is an offline dry-run plan and was not saved to a Pod.',
172
+ }),
173
+ };
174
+ }
175
+ }
176
+ function getObjLocalOutboxPath() {
177
+ return (0, path_1.join)((0, credentials_store_1.getSolidHomeDir)(), 'apps', 'xpod', 'outbox', 'obj-mutations.jsonl');
178
+ }
179
+ function appendObjLocalOutbox(entry, outboxPath = getObjLocalOutboxPath()) {
180
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(outboxPath), { recursive: true });
181
+ (0, fs_1.appendFileSync)(outboxPath, `${JSON.stringify(entry)}\n`, 'utf-8');
182
+ }
115
183
  function varName(field) {
116
184
  return `v_${field.replace(/[^A-Za-z0-9_]/gu, '_')}`;
117
185
  }
@@ -159,6 +227,47 @@ function redactDescriptorObject(descriptor, value) {
159
227
  }
160
228
  return redacted;
161
229
  }
230
+ function serializeSchemaDescriptor(descriptor) {
231
+ const discovery = descriptor;
232
+ return {
233
+ schema: descriptor.uri,
234
+ uri: descriptor.uri,
235
+ alias: descriptorAliases(descriptor)[0] ?? descriptor.resourceKind,
236
+ aliases: descriptorAliases(descriptor),
237
+ domains: discovery.domains ?? [],
238
+ version: descriptor.version,
239
+ source: descriptor.source,
240
+ trustLevel: descriptor.trustLevel,
241
+ namespace: descriptor.namespace,
242
+ class: descriptor.class,
243
+ resourceKind: descriptor.resourceKind,
244
+ description: descriptor.description,
245
+ storage: descriptor.storage,
246
+ idSemantics: discovery.idSemantics ?? null,
247
+ relationFields: discovery.relationFields ?? Object.entries(descriptor.fields)
248
+ .filter(([, field]) => field.type === 'uri')
249
+ .map(([field]) => field),
250
+ documentPathPolicy: discovery.documentPathPolicy ?? null,
251
+ fields: descriptor.fields,
252
+ requiredFields: Object.entries(descriptor.fields)
253
+ .filter(([, field]) => field.required)
254
+ .map(([field]) => field),
255
+ uniqueBy: descriptor.uniqueBy,
256
+ writableFields: descriptor.writableFields,
257
+ mergePolicy: descriptor.mergePolicy,
258
+ exampleInput: discovery.exampleInput ?? descriptor.examples[0] ?? null,
259
+ examples: descriptor.examples,
260
+ };
261
+ }
262
+ function listSchemaDescriptors(input = {}, descriptors = models_1.podSchema.list()) {
263
+ return descriptors
264
+ .filter((descriptor) => !input.domain || (descriptor.domains ?? []).includes(input.domain))
265
+ .map(serializeSchemaDescriptor);
266
+ }
267
+ function describeSchemaDescriptor(schemaOrAlias, descriptors = models_1.podSchema.list()) {
268
+ const descriptor = descriptors.find((item) => descriptorMatchesRef(item, schemaOrAlias));
269
+ return descriptor ? serializeSchemaDescriptor(descriptor) : null;
270
+ }
162
271
  function buildDescriptorUpsertSparql(descriptor, subject, row) {
163
272
  const fields = descriptor.fields;
164
273
  const match = row.match ?? {};
@@ -167,15 +276,17 @@ function buildDescriptorUpsertSparql(descriptor, subject, row) {
167
276
  assertWritableFields(descriptor, set);
168
277
  const merged = { ...match, ...set };
169
278
  const deleteFields = Object.keys(set).filter((field) => fields[field]);
170
- const deletes = deleteFields.map((field) => `<${subject}> <${fields[field].predicate}> ?old_${field.replace(/[^A-Za-z0-9_]/gu, '_')} .`);
171
- const optionals = deleteFields.map((field) => `OPTIONAL { <${subject}> <${fields[field].predicate}> ?old_${field.replace(/[^A-Za-z0-9_]/gu, '_')} }`);
279
+ const deletes = deleteFields.map((field) => `DELETE WHERE {\n <${subject}> <${fields[field].predicate}> ?old_${field.replace(/[^A-Za-z0-9_]/gu, '_')} .\n}`);
172
280
  const inserts = [
173
281
  `<${subject}> a <${descriptor.class}>`,
174
282
  ...Object.entries(merged)
175
283
  .filter(([field, value]) => fields[field] && value !== undefined && value !== null)
176
284
  .map(([field, value]) => `<${subject}> <${fields[field].predicate}> ${sparqlValue(value, fields[field])}`),
177
285
  ];
178
- return `DELETE {\n ${deletes.join('\n ')}\n}\nINSERT {\n ${inserts.join(' .\n ')} .\n}\nWHERE {\n ${optionals.join('\n ')}\n}`;
286
+ return [
287
+ ...deletes,
288
+ `INSERT DATA {\n ${inserts.join(' .\n ')} .\n}`,
289
+ ].join(';\n');
179
290
  }
180
291
  function buildDescriptorPatchSparql(descriptor, subject, set) {
181
292
  return buildDescriptorUpsertSparql(descriptor, subject, { set });
@@ -382,6 +493,42 @@ function bindingToValue(binding, field) {
382
493
  }
383
494
  return binding.value;
384
495
  }
496
+ function descriptorObjectFromTurtle(descriptor, subject, resourceUrl, turtle) {
497
+ const quads = new n3_1.Parser({ baseIRI: resourceUrl }).parse(turtle);
498
+ const object = {};
499
+ let hasDescriptorType = false;
500
+ for (const quad of quads) {
501
+ if (quad.subject.value !== subject)
502
+ continue;
503
+ if (quad.predicate.value === 'http://www.w3.org/1999/02/22-rdf-syntax-ns#type' &&
504
+ quad.object.value === descriptor.class) {
505
+ hasDescriptorType = true;
506
+ }
507
+ }
508
+ for (const [field, descriptorField] of Object.entries(descriptor.fields)) {
509
+ const quad = quads.find((item) => item.subject.value === subject &&
510
+ item.predicate.value === descriptorField.predicate);
511
+ if (!quad)
512
+ continue;
513
+ const value = bindingToValue({
514
+ type: quad.object.termType === 'NamedNode' ? 'uri' : 'literal',
515
+ value: quad.object.value,
516
+ datatype: 'datatype' in quad.object ? quad.object.datatype.value : undefined,
517
+ }, descriptorField);
518
+ if (value !== undefined) {
519
+ object[field] = descriptorField.secret ? '[redacted]' : value;
520
+ }
521
+ }
522
+ if (!hasDescriptorType && Object.keys(object).length === 0) {
523
+ return null;
524
+ }
525
+ return {
526
+ schema: descriptor.uri,
527
+ subject,
528
+ resourceUrl,
529
+ object,
530
+ };
531
+ }
385
532
  async function applyEtags(context, objects) {
386
533
  const cache = new Map();
387
534
  for (const object of objects) {
@@ -417,6 +564,12 @@ async function queryDescriptorObjects(context, selector) {
417
564
  },
418
565
  body: query,
419
566
  });
567
+ if (!response.ok) {
568
+ const fallback = await queryDescriptorObjectFromResource(context, selector);
569
+ if (fallback) {
570
+ return fallback;
571
+ }
572
+ }
420
573
  (0, resource_1.ensureOk)(response, 'obj_query_failed', `Failed to list objects for ${selector.descriptor.uri}`);
421
574
  const result = (await response.json());
422
575
  const objects = (result.results?.bindings ?? []).flatMap((binding) => {
@@ -442,6 +595,31 @@ async function queryDescriptorObjects(context, selector) {
442
595
  }
443
596
  return objects;
444
597
  }
598
+ async function queryDescriptorObjectFromResource(context, selector) {
599
+ if (!selector.subject)
600
+ return null;
601
+ if (Object.keys(selector.where).length > 0)
602
+ return null;
603
+ if (Object.keys(selector.relations).length > 0)
604
+ return null;
605
+ const resourceUrl = (0, rdf_1.documentResourceInput)(selector.subject);
606
+ const target = (0, resource_1.resolveResourceTarget)(context, resourceUrl);
607
+ const response = await (0, resource_1.fetchResource)(context, target, {
608
+ method: 'GET',
609
+ headers: { Accept: 'text/turtle' },
610
+ });
611
+ if (response.status === 404)
612
+ return [];
613
+ if (!response.ok)
614
+ return null;
615
+ const turtle = await response.text();
616
+ const object = descriptorObjectFromTurtle(selector.descriptor, selector.subject, target.resourceUrl, turtle);
617
+ const objects = object ? [object] : [];
618
+ if (selector.includeMetadata) {
619
+ await applyEtags(context, objects);
620
+ }
621
+ return objects;
622
+ }
445
623
  function jsonlForObjects(objects) {
446
624
  return `${objects.map((object, index) => JSON.stringify({
447
625
  index,
@@ -701,36 +879,51 @@ async function executeDescriptorRow(context, row, index, commit) {
701
879
  }
702
880
  throw new output_1.CliCommandError('unsupported_operation', `Unsupported object operation: ${op}`, 2);
703
881
  }
704
- const storage = (0, models_1.createPodStorage)();
882
+ const item = descriptorUpsertPlanItem({
883
+ descriptor,
884
+ index,
885
+ commit,
886
+ podRoot: context.podRoot,
887
+ row,
888
+ });
889
+ const subject = String(item.subject);
890
+ if (commit) {
891
+ const sparql = buildDescriptorUpsertSparql(descriptor, subject, row);
892
+ await patchSubject({ context, subject, sparql, ifMatch: row.ifMatch, errorCode: 'obj_commit_failed' });
893
+ }
894
+ return item;
895
+ }
896
+ function descriptorUpsertPlanItem(input) {
897
+ const storage = (0, models_1.createPodStorage)((0, models_1.createPodModelDescriptorRegistry)([input.descriptor]));
705
898
  const validation = storage.validate({
706
- schemaUri: descriptor.uri,
899
+ schemaUri: input.descriptor.uri,
707
900
  operation: 'upsert',
708
- match: row.match ?? {},
709
- set: row.set ?? {},
901
+ match: input.row.match ?? {},
902
+ set: input.row.set ?? {},
710
903
  });
711
904
  if (!validation.ok) {
712
905
  throw new output_1.CliCommandError(validation.error.code, validation.error.message, 2);
713
906
  }
714
- const subject = resourceUrlFromPlan(context.podRoot, validation.plan);
715
- if (commit) {
716
- const sparql = buildDescriptorUpsertSparql(descriptor, subject, row);
717
- await patchSubject({ context, subject, sparql, ifMatch: row.ifMatch, errorCode: 'obj_commit_failed' });
718
- }
907
+ const discovery = input.descriptor;
908
+ const subject = resourceUrlFromPlan(input.podRoot, validation.plan);
719
909
  return {
720
- index,
910
+ index: input.index,
721
911
  ok: true,
722
- code: commit ? 'committed' : 'plan_ready',
723
- operation: op,
724
- schema: descriptor.uri,
912
+ code: input.commit ? 'committed' : 'plan_ready',
913
+ operation: input.row.op ?? 'upsert',
914
+ schema: input.descriptor.uri,
725
915
  subject,
726
916
  resourceUrl: (0, rdf_1.documentResourceInput)(subject),
727
917
  planId: validation.plan.id,
728
- match: redactDescriptorObject(descriptor, row.match ?? {}),
729
- set: redactDescriptorObject(descriptor, row.set ?? {}),
918
+ resourceId: validation.plan.resourceId,
919
+ resourceUri: validation.plan.resourceUri,
920
+ documentPathPolicy: discovery.documentPathPolicy ?? null,
921
+ match: redactDescriptorObject(input.descriptor, input.row.match ?? {}),
922
+ set: redactDescriptorObject(input.descriptor, input.row.set ?? {}),
730
923
  };
731
924
  }
732
925
  async function executeRows(input) {
733
- const context = await (0, auth_context_1.requireAuthContext)(input.argv);
926
+ const context = await resolveObjMutationContext(input.argv, input.commit);
734
927
  const items = [];
735
928
  for (const [index, originalRow] of input.rows.entries()) {
736
929
  const row = {
@@ -738,10 +931,44 @@ async function executeRows(input) {
738
931
  schema: originalRow.schema ?? input.defaultSchema,
739
932
  };
740
933
  try {
934
+ const shouldCommitToPod = input.commit && !context.pendingOnly;
741
935
  const item = row.schema
742
- ? await executeDescriptorRow(context, row, index, input.commit)
743
- : await executeRawRow(context, row, index, input.commit);
744
- items.push(item);
936
+ ? await executeDescriptorRow(context, row, index, shouldCommitToPod)
937
+ : await executeRawRow(context, row, index, shouldCommitToPod);
938
+ if (context.pendingOnly && input.commit) {
939
+ const outboxPath = context.outboxPath ?? getObjLocalOutboxPath();
940
+ appendObjLocalOutbox({
941
+ kind: 'xpod.obj.mutation',
942
+ version: 1,
943
+ status: 'pending',
944
+ createdAt: new Date().toISOString(),
945
+ webId: context.webId,
946
+ podRoot: context.podRoot,
947
+ row,
948
+ item,
949
+ }, outboxPath);
950
+ items.push({
951
+ ...item,
952
+ code: 'pending_local',
953
+ pendingLocal: true,
954
+ outboxPath,
955
+ warnings: [
956
+ ...(Array.isArray(item.warnings) ? item.warnings : []),
957
+ context.pendingWarning,
958
+ ],
959
+ });
960
+ continue;
961
+ }
962
+ items.push(context.planningOnly
963
+ ? {
964
+ ...item,
965
+ planningOnly: true,
966
+ warnings: [
967
+ ...(Array.isArray(item.warnings) ? item.warnings : []),
968
+ context.planningWarning,
969
+ ],
970
+ }
971
+ : item);
745
972
  }
746
973
  catch (error) {
747
974
  const err = error instanceof output_1.CliCommandError
@@ -752,11 +979,61 @@ async function executeRows(input) {
752
979
  }
753
980
  return items;
754
981
  }
982
+ function mutationItemsResultCode(items, commit) {
983
+ if (!items.every((item) => item.ok))
984
+ return 'partial_failure';
985
+ if (items.some((item) => item.code === 'pending_local'))
986
+ return 'pending_local';
987
+ return commit ? 'committed' : 'plan_ready';
988
+ }
755
989
  function printItems(items) {
756
990
  for (const item of items) {
757
991
  console.log(`${item.index}\t${item.ok ? item.code : `ERROR ${item.code}`}\t${String(item.subject ?? item.resourceUrl ?? item.message ?? '')}`);
758
992
  }
759
993
  }
994
+ const schemasCommand = {
995
+ command: 'schemas',
996
+ describe: 'List model-backed object schemas available to AI tools',
997
+ builder: (yargs) => objOptions(yargs)
998
+ .option('domain', { type: 'string', description: 'Filter schemas by domain such as capture or symphony' }),
999
+ handler: async (argv) => {
1000
+ try {
1001
+ const schemas = listSchemaDescriptors({ domain: argv.domain });
1002
+ if (argv.json) {
1003
+ (0, output_1.writeJsonResult)({ schemas }, 'schemas');
1004
+ return;
1005
+ }
1006
+ for (const descriptor of schemas) {
1007
+ console.log(`${String(descriptor.alias)}\t${String(descriptor.schema)}\t${String(descriptor.resourceKind)}`);
1008
+ }
1009
+ }
1010
+ catch (error) {
1011
+ (0, output_1.handleCliError)(error, argv.json);
1012
+ }
1013
+ },
1014
+ };
1015
+ const describeCommand = {
1016
+ command: 'describe <schema>',
1017
+ describe: 'Describe one model-backed object schema by URI or alias',
1018
+ builder: (yargs) => objOptions(yargs)
1019
+ .positional('schema', { type: 'string', demandOption: true, description: 'Schema URI or descriptor alias' }),
1020
+ handler: async (argv) => {
1021
+ try {
1022
+ const descriptor = describeSchemaDescriptor(argv.schema);
1023
+ if (!descriptor) {
1024
+ throw new output_1.CliCommandError('schema_unknown', `Schema is not known by @undefineds.co/models: ${argv.schema}`, 2);
1025
+ }
1026
+ if (argv.json) {
1027
+ (0, output_1.writeJsonResult)({ descriptor }, 'schema');
1028
+ return;
1029
+ }
1030
+ console.log(JSON.stringify(descriptor, null, 2));
1031
+ }
1032
+ catch (error) {
1033
+ (0, output_1.handleCliError)(error, argv.json);
1034
+ }
1035
+ },
1036
+ };
760
1037
  const importCommand = {
761
1038
  command: 'import <file>',
762
1039
  describe: 'Import descriptor-backed or explicit raw JSONL rows',
@@ -772,9 +1049,7 @@ const importCommand = {
772
1049
  rows: await readJsonl(argv.file),
773
1050
  commit: argv.commit === true,
774
1051
  });
775
- const code = items.every((item) => item.ok)
776
- ? (argv.commit ? 'committed' : 'plan_ready')
777
- : 'partial_failure';
1052
+ const code = mutationItemsResultCode(items, argv.commit === true);
778
1053
  if (argv.json) {
779
1054
  (0, output_1.writeJsonItems)(items, code);
780
1055
  return;
@@ -794,6 +1069,7 @@ const upsertCommand = {
794
1069
  builder: (yargs) => objOptions(yargs)
795
1070
  .option('schema', { type: 'string', demandOption: true, description: 'Schema URI or descriptor alias' })
796
1071
  .option('from', { type: 'string', demandOption: true, description: 'JSONL file to read, or - for stdin' })
1072
+ .nargs('from', 1)
797
1073
  .option('dry-run', { type: 'boolean', description: 'Validate and print a plan without writing' })
798
1074
  .option('commit', { type: 'boolean', description: 'Commit the validated mutations' })
799
1075
  .check(mutationModeCheck),
@@ -805,9 +1081,7 @@ const upsertCommand = {
805
1081
  defaultSchema: resolveDescriptor(argv.schema).uri,
806
1082
  commit: argv.commit === true,
807
1083
  });
808
- const code = items.every((item) => item.ok)
809
- ? (argv.commit ? 'committed' : 'plan_ready')
810
- : 'partial_failure';
1084
+ const code = mutationItemsResultCode(items, argv.commit === true);
811
1085
  if (argv.json) {
812
1086
  (0, output_1.writeJsonItems)(items, code);
813
1087
  return;
@@ -1044,6 +1318,8 @@ exports.objCommand = {
1044
1318
  command: 'obj',
1045
1319
  describe: 'Descriptor-backed object transport',
1046
1320
  builder: (yargs) => yargs
1321
+ .command(schemasCommand)
1322
+ .command(describeCommand)
1047
1323
  .command(exportCommand)
1048
1324
  .command(importCommand)
1049
1325
  .command(getCommand)