artifact-cafe 0.0.15 → 0.0.16

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 (2) hide show
  1. package/dist/index.js +1006 -394
  2. package/package.json +9 -9
package/dist/index.js CHANGED
@@ -497,245 +497,573 @@ function elementLabel(el) {
497
497
  return classes.length ? `<${el.tag}.${classes.join(".")}>` : `<${el.tag}>`;
498
498
  }
499
499
 
500
- // ../shared/src/errors.ts
500
+ // ../shared/src/data.ts
501
+ var DATA_MANIFEST_PATH = ".artifact/data.json";
502
+ var DATA_NAME_RE = /^[a-z][a-z0-9_]*$/;
503
+ var DATA_NAME_MAX = 64;
504
+ var DATA_RESERVED_FIELDS = /* @__PURE__ */ new Set([
505
+ "id",
506
+ "created_at",
507
+ "updated_at",
508
+ "created_by",
509
+ "mine"
510
+ ]);
511
+ var DATA_LIMITS = {
512
+ artifactRateWindowSeconds: 3600,
513
+ artifactReadHourlyLimit: 1e4,
514
+ artifactWriteHourlyLimit: 1e3,
515
+ defaultReadRate: "120/minute/ip",
516
+ defaultWriteRate: "10/minute/ip",
517
+ maxCollections: 10,
518
+ maxFieldsPerCollection: 50,
519
+ maxListLimit: 200,
520
+ maxManifestBytes: 64 * 1024,
521
+ maxRecordBytes: 16 * 1024,
522
+ maxRecordsPerArtifact: 1e4
523
+ };
524
+ var DATA_ACCESS_DEFAULTS = {
525
+ delete: "owner",
526
+ insert: "owner",
527
+ read: "public",
528
+ update: "owner"
529
+ };
530
+ var DATA_RATE_LIMIT_RE = /^([1-9]\d{0,5})\/(hour|minute)\/ip$/;
531
+ var isPlainObject = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
532
+ function validateFieldValue(name, field, value, errors) {
533
+ switch (field.type) {
534
+ case "string": {
535
+ if (typeof value !== "string") {
536
+ errors.push(`${name}: expected a string`);
537
+ return value;
538
+ }
539
+ const s = field.trim ? value.trim() : value;
540
+ if (field.maxLength !== void 0 && s.length > field.maxLength) {
541
+ errors.push(`${name}: longer than maxLength ${field.maxLength}`);
542
+ }
543
+ return s;
544
+ }
545
+ case "number":
546
+ case "integer": {
547
+ if (typeof value !== "number" || Number.isNaN(value) || !Number.isFinite(value)) {
548
+ errors.push(`${name}: expected a ${field.type}`);
549
+ return value;
550
+ }
551
+ if (field.type === "integer" && !Number.isInteger(value)) {
552
+ errors.push(`${name}: expected an integer`);
553
+ }
554
+ if (field.minimum !== void 0 && value < field.minimum) {
555
+ errors.push(`${name}: below minimum ${field.minimum}`);
556
+ }
557
+ if (field.maximum !== void 0 && value > field.maximum) {
558
+ errors.push(`${name}: above maximum ${field.maximum}`);
559
+ }
560
+ return value;
561
+ }
562
+ case "boolean": {
563
+ if (typeof value !== "boolean") {
564
+ errors.push(`${name}: expected a boolean`);
565
+ }
566
+ return value;
567
+ }
568
+ case "datetime": {
569
+ if (typeof value !== "string" || Number.isNaN(Date.parse(value))) {
570
+ errors.push(`${name}: expected an ISO 8601 datetime string`);
571
+ }
572
+ return value;
573
+ }
574
+ case "url": {
575
+ if (typeof value !== "string" || !canParseUrl(value)) {
576
+ errors.push(`${name}: expected a valid URL`);
577
+ }
578
+ return value;
579
+ }
580
+ case "email": {
581
+ if (typeof value !== "string" || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
582
+ errors.push(`${name}: expected an email address`);
583
+ }
584
+ return value;
585
+ }
586
+ case "array": {
587
+ if (!Array.isArray(value)) {
588
+ errors.push(`${name}: expected an array`);
589
+ return value;
590
+ }
591
+ if (field.maxLength !== void 0 && value.length > field.maxLength) {
592
+ errors.push(`${name}: more than maxLength ${field.maxLength} items`);
593
+ }
594
+ return value;
595
+ }
596
+ case "object": {
597
+ if (!isPlainObject(value)) {
598
+ errors.push(`${name}: expected an object`);
599
+ }
600
+ return value;
601
+ }
602
+ default: {
603
+ errors.push(`${name}: unknown field type`);
604
+ return value;
605
+ }
606
+ }
607
+ }
608
+ function canParseUrl(value) {
609
+ try {
610
+ return Boolean(new URL(value));
611
+ } catch {
612
+ return false;
613
+ }
614
+ }
615
+ function validateRecordInput(collection, input, opts = {}) {
616
+ if (!isPlainObject(input)) {
617
+ return { errors: ["record body must be a JSON object"], ok: false };
618
+ }
619
+ const errors = [];
620
+ const data2 = {};
621
+ for (const key of Object.keys(input)) {
622
+ if (DATA_RESERVED_FIELDS.has(key)) {
623
+ errors.push(`${key}: reserved field (server-stamped)`);
624
+ continue;
625
+ }
626
+ const field = collection.fields[key];
627
+ if (!(Object.hasOwn(collection.fields, key) && field)) {
628
+ errors.push(`${key}: not a declared field`);
629
+ continue;
630
+ }
631
+ const value = input[key];
632
+ if (value === null || value === void 0) {
633
+ if (!field.required) {
634
+ data2[key] = null;
635
+ } else if (opts.partial) {
636
+ errors.push(`${key}: required (cannot be null)`);
637
+ }
638
+ continue;
639
+ }
640
+ data2[key] = validateFieldValue(key, field, value, errors);
641
+ }
642
+ if (opts.partial) {
643
+ if (Object.keys(data2).length === 0 && errors.length === 0) {
644
+ errors.push("no fields to update");
645
+ }
646
+ } else {
647
+ for (const [name, field] of Object.entries(collection.fields)) {
648
+ const present = data2[name] !== void 0 && data2[name] !== null;
649
+ if (present) {
650
+ continue;
651
+ }
652
+ if (field.default !== void 0) {
653
+ data2[name] = field.default;
654
+ } else if (field.required) {
655
+ errors.push(`${name}: required`);
656
+ }
657
+ }
658
+ }
659
+ if (errors.length > 0) {
660
+ return { errors, ok: false };
661
+ }
662
+ return { data: data2, ok: true };
663
+ }
664
+
665
+ // ../shared/src/data-api.ts
501
666
  import { z as z8 } from "zod";
502
- var apiErrorSchema = z8.object({
503
- code: z8.string(),
504
- docsUrl: z8.string().url(),
505
- message: z8.string(),
506
- retryAfter: z8.number().int().positive().optional()
667
+ var dataRecordSchema = z8.object({
668
+ created_at: z8.string(),
669
+ created_by: z8.string().nullable(),
670
+ id: z8.string(),
671
+ updated_at: z8.string()
672
+ }).catchall(z8.unknown());
673
+ var dataPublicRecordSchema = z8.object({
674
+ created_at: z8.string(),
675
+ created_by: z8.never().optional(),
676
+ id: z8.string(),
677
+ mine: z8.boolean(),
678
+ updated_at: z8.string()
679
+ }).catchall(z8.unknown());
680
+ var dataPublicListResponseSchema = z8.object({
681
+ nextCursor: z8.string().nullable(),
682
+ records: z8.array(dataPublicRecordSchema)
683
+ });
684
+ var dataListResponseSchema = z8.object({
685
+ nextCursor: z8.string().nullable(),
686
+ records: z8.array(dataRecordSchema)
687
+ });
688
+ var dataCollectionSummarySchema = z8.object({
689
+ access: z8.record(z8.string(), z8.string()).optional(),
690
+ count: z8.number().int().nonnegative().optional(),
691
+ name: z8.string(),
692
+ rateLimit: z8.string().optional()
693
+ });
694
+ var dataSummaryResponseSchema = z8.object({
695
+ collections: z8.array(dataCollectionSummarySchema),
696
+ enabled: z8.boolean()
697
+ });
698
+ var dataClearResponseSchema = z8.object({
699
+ cleared: z8.number().int().nonnegative()
507
700
  });
508
701
 
509
- // ../shared/src/feedback.ts
702
+ // ../shared/src/data-manifest.ts
510
703
  import { z as z9 } from "zod";
511
- var feedbackRequestSchema = z9.object({
512
- email: z9.string().trim().toLowerCase().email().optional().or(z9.literal("")),
513
- message: z9.string().trim().min(1).max(4e3)
704
+ var nameSchema = z9.string().max(DATA_NAME_MAX).regex(DATA_NAME_RE, "must match ^[a-z][a-z0-9_]*$");
705
+ var fieldTypeSchema = z9.enum([
706
+ "string",
707
+ "number",
708
+ "integer",
709
+ "boolean",
710
+ "datetime",
711
+ "url",
712
+ "email",
713
+ "array",
714
+ "object"
715
+ ]);
716
+ var fieldSchema = z9.strictObject({
717
+ default: z9.unknown().optional(),
718
+ maximum: z9.number().optional(),
719
+ maxLength: z9.number().int().positive().optional(),
720
+ minimum: z9.number().optional(),
721
+ required: z9.boolean().optional(),
722
+ trim: z9.boolean().optional(),
723
+ type: fieldTypeSchema
724
+ }).superRefine((field, ctx) => {
725
+ if (field.default === void 0) {
726
+ return;
727
+ }
728
+ if (!defaultMatchesType(field)) {
729
+ ctx.addIssue({
730
+ code: "custom",
731
+ message: `default value does not match type "${field.type}"`,
732
+ path: ["default"]
733
+ });
734
+ }
735
+ });
736
+ function defaultMatchesType(field) {
737
+ if (field.required && field.default === null) {
738
+ return false;
739
+ }
740
+ return validateRecordInput(
741
+ {
742
+ access: DATA_ACCESS_DEFAULTS,
743
+ fields: { value: { ...field, default: void 0 } }
744
+ },
745
+ { value: field.default }
746
+ ).ok;
747
+ }
748
+ var accessLevelSchema = z9.enum(["public", "creator", "owner", "none"]);
749
+ var accessSchema = z9.strictObject({
750
+ delete: accessLevelSchema.default("owner"),
751
+ insert: accessLevelSchema.default("owner"),
752
+ read: accessLevelSchema.default("public"),
753
+ update: accessLevelSchema.default("owner")
754
+ }).prefault({});
755
+ var collectionSchema = z9.strictObject({
756
+ access: accessSchema,
757
+ fields: z9.record(nameSchema, fieldSchema),
758
+ publicMutation: z9.literal("open").optional(),
759
+ rateLimit: z9.string().regex(DATA_RATE_LIMIT_RE, 'expected "N/hour/ip" or "N/minute/ip"').optional()
760
+ }).superRefine((collection, ctx) => {
761
+ const fieldNames = Object.keys(collection.fields);
762
+ if (fieldNames.length > DATA_LIMITS.maxFieldsPerCollection) {
763
+ ctx.addIssue({
764
+ code: "custom",
765
+ message: `at most ${DATA_LIMITS.maxFieldsPerCollection} fields per collection`,
766
+ path: ["fields"]
767
+ });
768
+ }
769
+ for (const name of fieldNames) {
770
+ if (DATA_RESERVED_FIELDS.has(name)) {
771
+ ctx.addIssue({
772
+ code: "custom",
773
+ message: `"${name}" is reserved (server-stamped)`,
774
+ path: ["fields", name]
775
+ });
776
+ }
777
+ }
778
+ for (const action of ["update", "delete"]) {
779
+ if (collection.access[action] === "public" && collection.publicMutation !== "open") {
780
+ ctx.addIssue({
781
+ code: "custom",
782
+ message: `access.${action} may only be "public" alongside "publicMutation": "open"`,
783
+ path: ["access", action]
784
+ });
785
+ }
786
+ }
787
+ });
788
+ var dataManifestSchema = z9.strictObject({
789
+ collections: z9.record(nameSchema, collectionSchema)
790
+ }).superRefine((manifest, ctx) => {
791
+ if (Object.keys(manifest.collections).length > DATA_LIMITS.maxCollections) {
792
+ ctx.addIssue({
793
+ code: "custom",
794
+ message: `at most ${DATA_LIMITS.maxCollections} collections`,
795
+ path: ["collections"]
796
+ });
797
+ }
798
+ });
799
+ function parseDataManifest(text) {
800
+ if (new TextEncoder().encode(text).length > DATA_LIMITS.maxManifestBytes) {
801
+ return {
802
+ errors: [`manifest larger than ${DATA_LIMITS.maxManifestBytes} bytes`],
803
+ ok: false
804
+ };
805
+ }
806
+ let raw;
807
+ try {
808
+ raw = JSON.parse(text);
809
+ } catch (err) {
810
+ return {
811
+ errors: [`not valid JSON: ${err instanceof Error ? err.message : err}`],
812
+ ok: false
813
+ };
814
+ }
815
+ const parsed = dataManifestSchema.safeParse(raw);
816
+ if (!parsed.success) {
817
+ return {
818
+ errors: parsed.error.issues.map(
819
+ (issue) => issue.path.length ? `${issue.path.join(".")}: ${issue.message}` : issue.message
820
+ ),
821
+ ok: false
822
+ };
823
+ }
824
+ return { config: parsed.data, ok: true };
825
+ }
826
+
827
+ // ../shared/src/errors.ts
828
+ import { z as z10 } from "zod";
829
+ var apiErrorSchema = z10.object({
830
+ code: z10.string(),
831
+ docsUrl: z10.string().url(),
832
+ message: z10.string(),
833
+ retryAfter: z10.number().int().positive().optional(),
834
+ scope: z10.enum(["ip", "artifact"]).optional()
835
+ });
836
+
837
+ // ../shared/src/feedback.ts
838
+ import { z as z11 } from "zod";
839
+ var feedbackRequestSchema = z11.object({
840
+ email: z11.string().trim().toLowerCase().email().optional().or(z11.literal("")),
841
+ message: z11.string().trim().min(1).max(4e3)
514
842
  });
515
- var feedbackResponseSchema = z9.object({
516
- sent: z9.literal(true)
843
+ var feedbackResponseSchema = z11.object({
844
+ sent: z11.literal(true)
517
845
  });
518
846
 
519
847
  // ../shared/src/grants.ts
520
- import { z as z10 } from "zod";
521
- var grantRoleSchema = z10.enum(["viewer"]);
522
- var createGrantSchema = z10.object({
523
- email: z10.string().trim().toLowerCase().email().optional(),
848
+ import { z as z12 } from "zod";
849
+ var grantRoleSchema = z12.enum(["viewer"]);
850
+ var createGrantSchema = z12.object({
851
+ email: z12.string().trim().toLowerCase().email().optional(),
524
852
  role: grantRoleSchema.default("viewer"),
525
- userId: z10.string().min(1).optional()
853
+ userId: z12.string().min(1).optional()
526
854
  }).refine((body) => Boolean(body.email) !== Boolean(body.userId), {
527
855
  message: "provide exactly one of email or userId"
528
856
  });
529
- var grantSchema = z10.object({
530
- createdAt: z10.string(),
531
- email: z10.string().email().nullable(),
532
- expiresAt: z10.string().nullable(),
533
- id: z10.string(),
857
+ var grantSchema = z12.object({
858
+ createdAt: z12.string(),
859
+ email: z12.string().email().nullable(),
860
+ expiresAt: z12.string().nullable(),
861
+ id: z12.string(),
534
862
  // The subject's avatar once they have an account; null otherwise.
535
- image: z10.string().nullable(),
863
+ image: z12.string().nullable(),
536
864
  // Present on the invited account after redemption / when granting an
537
865
  // existing user; null for a still-pending email invite.
538
- name: z10.string().nullable(),
539
- redeemedAt: z10.string().nullable(),
866
+ name: z12.string().nullable(),
867
+ redeemedAt: z12.string().nullable(),
540
868
  role: grantRoleSchema,
541
869
  // Shown as "· pending" beside the role until the link is redeemed.
542
- status: z10.enum(["pending", "active"]),
870
+ status: z12.enum(["pending", "active"]),
543
871
  // Display fragment of the live invitation token, or null once redeemed.
544
- tokenPrefix: z10.string().nullable(),
545
- userId: z10.string().nullable()
872
+ tokenPrefix: z12.string().nullable(),
873
+ userId: z12.string().nullable()
546
874
  });
547
875
  var createdGrantSchema = grantSchema.extend({
548
- url: z10.string().url()
876
+ url: z12.string().url()
549
877
  });
550
- var grantListResponseSchema = z10.object({
551
- grants: z10.array(grantSchema),
878
+ var grantListResponseSchema = z12.object({
879
+ grants: z12.array(grantSchema),
552
880
  // The artifact's current general-access value, so the share surface can
553
881
  // render the "General access" line without a second read.
554
- visibility: z10.enum(["public", "unlisted", "workspace", "private"]),
882
+ visibility: z12.enum(["public", "unlisted", "workspace", "private"]),
555
883
  // The owning workspace's logo, so the share modal can draw the workspace
556
884
  // itself rather than a generic glyph. Null when none was uploaded.
557
- workspaceLogo: z10.string().nullable().optional(),
885
+ workspaceLogo: z12.string().nullable().optional(),
558
886
  // The owning workspace's display name, so "People with access" can name the
559
887
  // audience `workspace` visibility actually admits ("All Acme members") rather
560
888
  // than leaving an empty list to imply nobody. Null for a workspace-less
561
889
  // artifact; optional so an older server still parses.
562
- workspaceName: z10.string().nullable().optional()
890
+ workspaceName: z12.string().nullable().optional()
563
891
  });
564
- var revokeGrantResponseSchema = z10.object({
565
- revoked: z10.literal(true)
892
+ var revokeGrantResponseSchema = z12.object({
893
+ revoked: z12.literal(true)
566
894
  });
567
- var acceptGrantSchema = z10.object({
568
- token: z10.string().min(1)
895
+ var acceptGrantSchema = z12.object({
896
+ token: z12.string().min(1)
569
897
  });
570
- var acceptGrantResponseSchema = z10.object({
571
- artifactId: z10.string(),
898
+ var acceptGrantResponseSchema = z12.object({
899
+ artifactId: z12.string(),
572
900
  // The review shell URL to land on, carrying ?invited=1 for the banner.
573
- artifactUrl: z10.string().url(),
574
- slug: z10.string(),
575
- title: z10.string()
576
- });
577
- var workspaceGuestSchema = z10.object({
578
- artifacts: z10.array(
579
- z10.object({
580
- artifactId: z10.string(),
581
- grantId: z10.string(),
582
- slug: z10.string(),
583
- status: z10.enum(["pending", "active"]),
584
- title: z10.string()
901
+ artifactUrl: z12.string().url(),
902
+ slug: z12.string(),
903
+ title: z12.string()
904
+ });
905
+ var workspaceGuestSchema = z12.object({
906
+ artifacts: z12.array(
907
+ z12.object({
908
+ artifactId: z12.string(),
909
+ grantId: z12.string(),
910
+ slug: z12.string(),
911
+ status: z12.enum(["pending", "active"]),
912
+ title: z12.string()
585
913
  })
586
914
  ),
587
- email: z10.string().nullable(),
588
- image: z10.string().nullable(),
589
- name: z10.string().nullable(),
590
- userId: z10.string().nullable()
591
- });
592
- var workspaceGuestListResponseSchema = z10.object({
593
- guests: z10.array(workspaceGuestSchema)
594
- });
595
- var sharedWithMeArtifactSchema = z10.object({
596
- artifactId: z10.string(),
597
- slug: z10.string(),
598
- title: z10.string(),
599
- url: z10.string().url(),
915
+ email: z12.string().nullable(),
916
+ image: z12.string().nullable(),
917
+ name: z12.string().nullable(),
918
+ userId: z12.string().nullable()
919
+ });
920
+ var workspaceGuestListResponseSchema = z12.object({
921
+ guests: z12.array(workspaceGuestSchema)
922
+ });
923
+ var sharedWithMeArtifactSchema = z12.object({
924
+ artifactId: z12.string(),
925
+ slug: z12.string(),
926
+ title: z12.string(),
927
+ url: z12.string().url(),
600
928
  // Who shared it, for the "Shared with me" list.
601
- workspaceName: z10.string()
929
+ workspaceName: z12.string()
602
930
  });
603
- var sharedWithMeListResponseSchema = z10.object({
604
- artifacts: z10.array(sharedWithMeArtifactSchema)
931
+ var sharedWithMeListResponseSchema = z12.object({
932
+ artifacts: z12.array(sharedWithMeArtifactSchema)
605
933
  });
606
934
 
607
935
  // ../shared/src/html-meta.ts
608
936
  var HEAD_SCAN_CHARS = 64 * 1024;
609
937
 
610
938
  // ../shared/src/me.ts
611
- import { z as z12 } from "zod";
939
+ import { z as z14 } from "zod";
612
940
 
613
941
  // ../shared/src/workspaces.ts
614
- import { z as z11 } from "zod";
942
+ import { z as z13 } from "zod";
615
943
  var WORKSPACE_HANDLE_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,30}[a-z0-9])$/;
616
- var workspaceHandleUnavailableReasonSchema = z11.enum([
944
+ var workspaceHandleUnavailableReasonSchema = z13.enum([
617
945
  "held",
618
946
  "invalid",
619
947
  "reserved",
620
948
  "taken"
621
949
  ]);
622
- var workspaceHandleAvailabilitySchema = z11.object({
623
- available: z11.boolean(),
624
- handle: z11.string(),
950
+ var workspaceHandleAvailabilitySchema = z13.object({
951
+ available: z13.boolean(),
952
+ handle: z13.string(),
625
953
  // Human-readable sentence for the field hint; null when available.
626
- message: z11.string().nullable(),
954
+ message: z13.string().nullable(),
627
955
  reason: workspaceHandleUnavailableReasonSchema.nullable()
628
956
  });
629
- var workspaceRoleSchema = z11.enum(["owner", "admin", "member"]);
630
- var workspaceInviteRoleSchema = z11.enum(["admin", "member"]);
631
- var workspaceHandleSchema = z11.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
957
+ var workspaceRoleSchema = z13.enum(["owner", "admin", "member"]);
958
+ var workspaceInviteRoleSchema = z13.enum(["admin", "member"]);
959
+ var workspaceHandleSchema = z13.string().min(3).max(32).regex(WORKSPACE_HANDLE_PATTERN, {
632
960
  message: "handle must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
633
961
  });
634
- var createWorkspaceSchema = z11.object({
962
+ var createWorkspaceSchema = z13.object({
635
963
  handle: workspaceHandleSchema.optional(),
636
- name: z11.string().trim().min(1).max(100)
964
+ name: z13.string().trim().min(1).max(100)
637
965
  });
638
- var updateWorkspaceSchema = z11.object({
966
+ var updateWorkspaceSchema = z13.object({
639
967
  handle: workspaceHandleSchema.optional(),
640
- name: z11.string().trim().min(1).max(100).optional()
968
+ name: z13.string().trim().min(1).max(100).optional()
641
969
  }).refine((body) => body.handle !== void 0 || body.name !== void 0, {
642
970
  message: "provide at least one of name or handle"
643
971
  });
644
- var workspaceSchema = z11.object({
645
- createdAt: z11.string(),
646
- handle: z11.string(),
972
+ var workspaceSchema = z13.object({
973
+ createdAt: z13.string(),
974
+ handle: z13.string(),
647
975
  // ISO timestamp when `{handle}.artifact.cafe` was provisioned, or null.
648
976
  // Set by the one-time, self-serve enable flow (§4.4); never auto at
649
977
  // create, and always null for personal workspaces.
650
- hostnameProvisionedAt: z11.string().nullable(),
651
- id: z11.string(),
978
+ hostnameProvisionedAt: z13.string().nullable(),
979
+ id: z13.string(),
652
980
  // Auto-provisioned solo workspace, one per user: non-deletable, takes
653
981
  // no members, renameable like any workspace.
654
- isPersonal: z11.boolean(),
982
+ isPersonal: z13.boolean(),
655
983
  // Uploaded logo URL, or null for the initials fallback. Set through the
656
984
  // dedicated /workspaces/:id/avatar route, never the generic update.
657
- logo: z11.string().nullable(),
658
- name: z11.string(),
985
+ logo: z13.string().nullable(),
986
+ name: z13.string(),
659
987
  role: workspaceRoleSchema.nullable(),
660
- updatedAt: z11.string()
988
+ updatedAt: z13.string()
661
989
  });
662
- var workspaceListResponseSchema = z11.object({
663
- workspaces: z11.array(workspaceSchema)
990
+ var workspaceListResponseSchema = z13.object({
991
+ workspaces: z13.array(workspaceSchema)
664
992
  });
665
- var deleteWorkspaceResponseSchema = z11.object({
666
- deleted: z11.literal(true)
993
+ var deleteWorkspaceResponseSchema = z13.object({
994
+ deleted: z13.literal(true)
667
995
  });
668
- var workspaceMemberSchema = z11.object({
669
- createdAt: z11.string(),
670
- email: z11.string().email(),
671
- image: z11.string().nullable(),
672
- name: z11.string(),
996
+ var workspaceMemberSchema = z13.object({
997
+ createdAt: z13.string(),
998
+ email: z13.string().email(),
999
+ image: z13.string().nullable(),
1000
+ name: z13.string(),
673
1001
  role: workspaceRoleSchema,
674
- userId: z11.string()
1002
+ userId: z13.string()
675
1003
  });
676
- var workspaceMemberListResponseSchema = z11.object({
677
- members: z11.array(workspaceMemberSchema)
1004
+ var workspaceMemberListResponseSchema = z13.object({
1005
+ members: z13.array(workspaceMemberSchema)
678
1006
  });
679
- var updateWorkspaceMemberSchema = z11.object({
1007
+ var updateWorkspaceMemberSchema = z13.object({
680
1008
  role: workspaceRoleSchema
681
1009
  });
682
- var removeWorkspaceMemberResponseSchema = z11.object({
683
- removed: z11.literal(true)
1010
+ var removeWorkspaceMemberResponseSchema = z13.object({
1011
+ removed: z13.literal(true)
684
1012
  });
685
- var createWorkspaceInviteSchema = z11.object({
686
- email: z11.string().trim().toLowerCase().email().optional(),
687
- expiresAt: z11.string().datetime(),
688
- maxUses: z11.number().int().positive().optional(),
1013
+ var createWorkspaceInviteSchema = z13.object({
1014
+ email: z13.string().trim().toLowerCase().email().optional(),
1015
+ expiresAt: z13.string().datetime(),
1016
+ maxUses: z13.number().int().positive().optional(),
689
1017
  role: workspaceInviteRoleSchema
690
1018
  });
691
- var workspaceInviteSchema = z11.object({
692
- createdAt: z11.string(),
693
- email: z11.string().email().nullable(),
694
- expiresAt: z11.string(),
695
- id: z11.string(),
696
- maxUses: z11.number().int().positive().nullable(),
1019
+ var workspaceInviteSchema = z13.object({
1020
+ createdAt: z13.string(),
1021
+ email: z13.string().email().nullable(),
1022
+ expiresAt: z13.string(),
1023
+ id: z13.string(),
1024
+ maxUses: z13.number().int().positive().nullable(),
697
1025
  role: workspaceInviteRoleSchema,
698
- tokenPrefix: z11.string(),
699
- useCount: z11.number().int().nonnegative()
1026
+ tokenPrefix: z13.string(),
1027
+ useCount: z13.number().int().nonnegative()
700
1028
  });
701
1029
  var createdWorkspaceInviteSchema = workspaceInviteSchema.extend({
702
- url: z11.string().url()
1030
+ url: z13.string().url()
703
1031
  });
704
- var workspaceInviteListResponseSchema = z11.object({
705
- invites: z11.array(workspaceInviteSchema)
1032
+ var workspaceInviteListResponseSchema = z13.object({
1033
+ invites: z13.array(workspaceInviteSchema)
706
1034
  });
707
- var revokeWorkspaceInviteResponseSchema = z11.object({
708
- revoked: z11.literal(true)
1035
+ var revokeWorkspaceInviteResponseSchema = z13.object({
1036
+ revoked: z13.literal(true)
709
1037
  });
710
- var acceptWorkspaceInviteSchema = z11.object({
711
- token: z11.string().min(1)
1038
+ var acceptWorkspaceInviteSchema = z13.object({
1039
+ token: z13.string().min(1)
712
1040
  });
713
- var acceptWorkspaceInviteResponseSchema = z11.object({
1041
+ var acceptWorkspaceInviteResponseSchema = z13.object({
714
1042
  role: workspaceRoleSchema,
715
1043
  workspace: workspaceSchema
716
1044
  });
717
- var folderVisibilitySchema = z11.enum(["public", "workspace"]);
718
- var artifactVisibilitySchema = z11.enum([
1045
+ var folderVisibilitySchema = z13.enum(["public", "workspace"]);
1046
+ var artifactVisibilitySchema = z13.enum([
719
1047
  "public",
720
1048
  "unlisted",
721
1049
  "workspace",
722
1050
  "private"
723
1051
  ]);
724
- var createFolderSchema = z11.object({
1052
+ var createFolderSchema = z13.object({
725
1053
  // Optional custom icon; omitted (or null) leaves the default glyph.
726
1054
  emoji: iconEmojiSchema.optional(),
727
- name: z11.string().trim().min(1).max(100),
1055
+ name: z13.string().trim().min(1).max(100),
728
1056
  visibility: folderVisibilitySchema
729
1057
  });
730
- var updateFolderSchema = z11.object({
1058
+ var updateFolderSchema = z13.object({
731
1059
  // With `visibility`: bulk-update the folder's artifacts to match
732
1060
  // (SPEC-2 §3.4 "Changing a folder's visibility offers a bulk update").
733
- cascade: z11.boolean().optional(),
1061
+ cascade: z13.boolean().optional(),
734
1062
  // Explicit null clears the icon back to the default glyph, so this is
735
1063
  // a field where "sent as null" and "omitted" mean different things.
736
1064
  emoji: iconEmojiSchema.optional(),
737
- name: z11.string().trim().min(1).max(100).optional(),
738
- sortOrder: z11.number().int().min(0).optional(),
1065
+ name: z13.string().trim().min(1).max(100).optional(),
1066
+ sortOrder: z13.number().int().min(0).optional(),
739
1067
  visibility: folderVisibilitySchema.optional()
740
1068
  }).refine(
741
1069
  (body) => body.name !== void 0 || body.visibility !== void 0 || body.sortOrder !== void 0 || body.emoji !== void 0,
@@ -743,93 +1071,93 @@ var updateFolderSchema = z11.object({
743
1071
  ).refine((body) => !body.cascade || body.visibility !== void 0, {
744
1072
  message: "cascade requires visibility"
745
1073
  });
746
- var folderSchema = z11.object({
747
- createdAt: z11.string(),
1074
+ var folderSchema = z13.object({
1075
+ createdAt: z13.string(),
748
1076
  // Custom icon, or null to render the default folder glyph.
749
- emoji: z11.string().nullable(),
750
- id: z11.string(),
751
- name: z11.string(),
1077
+ emoji: z13.string().nullable(),
1078
+ id: z13.string(),
1079
+ name: z13.string(),
752
1080
  // Permalink segment for the public folder page (/w/{handle}/f/{slug}).
753
1081
  // Server-derived from the name; renames regenerate it.
754
- slug: z11.string(),
755
- sortOrder: z11.number().int(),
756
- updatedAt: z11.string(),
1082
+ slug: z13.string(),
1083
+ sortOrder: z13.number().int(),
1084
+ updatedAt: z13.string(),
757
1085
  visibility: folderVisibilitySchema,
758
- workspaceId: z11.string()
1086
+ workspaceId: z13.string()
759
1087
  });
760
- var folderListResponseSchema = z11.object({
761
- folders: z11.array(folderSchema)
1088
+ var folderListResponseSchema = z13.object({
1089
+ folders: z13.array(folderSchema)
762
1090
  });
763
- var deleteFolderResponseSchema = z11.object({
764
- deleted: z11.literal(true)
1091
+ var deleteFolderResponseSchema = z13.object({
1092
+ deleted: z13.literal(true)
765
1093
  });
766
- var workspaceArtifactSchema = z11.object({
767
- createdAt: z11.string(),
768
- currentVersionNumber: z11.number().int().nullable(),
1094
+ var workspaceArtifactSchema = z13.object({
1095
+ createdAt: z13.string(),
1096
+ currentVersionNumber: z13.number().int().nullable(),
769
1097
  // Custom icon, or null to render the default document glyph.
770
- emoji: z11.string().nullable(),
771
- folderId: z11.string().nullable(),
772
- id: z11.string(),
773
- openCommentCount: z11.number().int().nonnegative(),
1098
+ emoji: z13.string().nullable(),
1099
+ folderId: z13.string().nullable(),
1100
+ id: z13.string(),
1101
+ openCommentCount: z13.number().int().nonnegative(),
774
1102
  reviewMode: reviewModeSchema,
775
- slug: z11.string(),
776
- title: z11.string(),
777
- updatedAt: z11.string(),
778
- url: z11.string().url(),
779
- versionCount: z11.number().int().nonnegative(),
1103
+ slug: z13.string(),
1104
+ title: z13.string(),
1105
+ updatedAt: z13.string(),
1106
+ url: z13.string().url(),
1107
+ versionCount: z13.number().int().nonnegative(),
780
1108
  visibility: artifactVisibilitySchema
781
1109
  });
782
- var workspaceArtifactListResponseSchema = z11.object({
783
- artifacts: z11.array(workspaceArtifactSchema)
1110
+ var workspaceArtifactListResponseSchema = z13.object({
1111
+ artifacts: z13.array(workspaceArtifactSchema)
784
1112
  });
785
- var workspaceArtifactSearchResultSchema = z11.object({
786
- id: z11.string(),
787
- slug: z11.string(),
788
- title: z11.string()
1113
+ var workspaceArtifactSearchResultSchema = z13.object({
1114
+ id: z13.string(),
1115
+ slug: z13.string(),
1116
+ title: z13.string()
789
1117
  });
790
- var workspaceArtifactSearchResponseSchema = z11.object({
791
- results: z11.array(workspaceArtifactSearchResultSchema)
1118
+ var workspaceArtifactSearchResponseSchema = z13.object({
1119
+ results: z13.array(workspaceArtifactSearchResultSchema)
792
1120
  });
793
1121
 
794
1122
  // ../shared/src/me.ts
795
- var personSchema = z12.object({
796
- image: z12.string().nullable(),
797
- name: z12.string()
1123
+ var personSchema = z14.object({
1124
+ image: z14.string().nullable(),
1125
+ name: z14.string()
798
1126
  });
799
- var ownArtifactSchema = z12.object({
1127
+ var ownArtifactSchema = z14.object({
800
1128
  // Everyone else who has published a version, oldest first. Any member may
801
1129
  // publish a new one (SPEC-2 §5.1), so an artifact's byline is often more
802
1130
  // than one person and the card stacks their faces rather than crediting
803
1131
  // whoever shipped last. Excludes `publishedBy`; empty on a solo artifact.
804
- coPublishers: z12.array(personSchema),
805
- createdAt: z12.string(),
1132
+ coPublishers: z14.array(personSchema),
1133
+ createdAt: z14.string(),
806
1134
  // SPEC-2 §5.1 — who *made* the artifact: the author of its first
807
1135
  // finalized version, the same row `viewerCanManage` is decided on. Not
808
1136
  // the same person as `publishedBy` once an agent ships v2, which is why
809
1137
  // the dashboard filters on both. Null for a token-only publish.
810
- createdBy: z12.object({ image: z12.string().nullable(), name: z12.string() }).nullable(),
811
- currentVersionNumber: z12.number().nullable(),
1138
+ createdBy: z14.object({ image: z14.string().nullable(), name: z14.string() }).nullable(),
1139
+ currentVersionNumber: z14.number().nullable(),
812
1140
  // Custom icon, or null to render the default document glyph.
813
- emoji: z12.string().nullable(),
1141
+ emoji: z14.string().nullable(),
814
1142
  // Owned artifacts normally never expire; non-null only while an
815
1143
  // expiry is still set (e.g. an unclaimed publish surfaced here later).
816
- expiresAt: z12.string().nullable(),
1144
+ expiresAt: z14.string().nullable(),
817
1145
  // SPEC-2 §8.4 — the containing folder for workspace-owned artifacts;
818
1146
  // both null for personal artifacts. `folderId` drives the dashboard's
819
1147
  // move control and folder filter; `folderName` is the display label,
820
1148
  // `folderEmoji` its icon (null = the default folder glyph).
821
- folderEmoji: z12.string().nullable(),
822
- folderId: z12.string().nullable(),
823
- folderName: z12.string().nullable(),
1149
+ folderEmoji: z14.string().nullable(),
1150
+ folderId: z14.string().nullable(),
1151
+ folderName: z14.string().nullable(),
824
1152
  // Whether a publish token is still active — revocable from the
825
1153
  // dashboard (§6.18); the token itself is never returned.
826
- hasPublishToken: z12.boolean(),
1154
+ hasPublishToken: z14.boolean(),
827
1155
  // Owner preference: hide the version list from reviewers in the review
828
1156
  // shell. Managers still see it.
829
- hideVersions: z12.boolean(),
830
- id: z12.string(),
831
- openThreads: z12.number(),
832
- protected: z12.boolean(),
1157
+ hideVersions: z14.boolean(),
1158
+ id: z14.string(),
1159
+ openThreads: z14.number(),
1160
+ protected: z14.boolean(),
833
1161
  // Who published the current version (falling back to the claiming
834
1162
  // owner) — the dashboard shows a name and avatar per artifact so a
835
1163
  // shared workspace reads as people's work, not anonymous rows. Null
@@ -838,44 +1166,44 @@ var ownArtifactSchema = z12.object({
838
1166
  // SPEC-2 §5.8 — the review dial, so the dashboard's settings dialog can
839
1167
  // render current state without a second fetch.
840
1168
  reviewMode: reviewModeSchema,
841
- slug: z12.string(),
842
- title: z12.string(),
843
- totalViews: z12.number(),
1169
+ slug: z14.string(),
1170
+ title: z14.string(),
1171
+ totalViews: z14.number(),
844
1172
  // SPEC §6.20 — messages by others newer than the viewer's last-seen
845
1173
  // watermark. 0 for signed-out/API-key-less contexts.
846
- unreadComments: z12.number(),
847
- updatedAt: z12.string(),
848
- url: z12.string(),
849
- versionCount: z12.number(),
1174
+ unreadComments: z14.number(),
1175
+ updatedAt: z14.string(),
1176
+ url: z14.string(),
1177
+ versionCount: z14.number(),
850
1178
  // SPEC-2 §5.1 — whether the caller clears the manage bar for this
851
1179
  // artifact (workspace admin, or the person who first published it).
852
1180
  // Only a manager's folder move inherits the folder's visibility, so the
853
1181
  // dashboard warns exactly the people the change can actually affect, and
854
1182
  // only a manager is offered the Share entry (§5.7).
855
- viewerCanManage: z12.boolean(),
1183
+ viewerCanManage: z14.boolean(),
856
1184
  // SPEC-2 §5.2 — the artifact's own visibility, the single enforcement
857
1185
  // source. The dashboard needs it to tell whether a folder move would
858
1186
  // widen exposure before it sends the PATCH (§3.4), and to render the
859
1187
  // audience glyph — one per level — on rows and cards (§5.7).
860
1188
  visibility: artifactVisibilitySchema
861
1189
  });
862
- var ownArtifactsResponseSchema = z12.object({
863
- artifacts: z12.array(ownArtifactSchema)
1190
+ var ownArtifactsResponseSchema = z14.object({
1191
+ artifacts: z14.array(ownArtifactSchema)
864
1192
  });
865
- var revokePublishTokenResponseSchema = z12.object({
866
- revoked: z12.boolean()
1193
+ var revokePublishTokenResponseSchema = z14.object({
1194
+ revoked: z14.boolean()
867
1195
  });
868
1196
 
869
1197
  // ../shared/src/owner.ts
870
- import { z as z13 } from "zod";
1198
+ import { z as z15 } from "zod";
871
1199
  var SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]{1,61}[a-z0-9])$/;
872
- var claimResponseSchema = z13.object({
873
- artifactId: z13.string(),
874
- slug: z13.string(),
875
- title: z13.string(),
876
- url: z13.string().url()
1200
+ var claimResponseSchema = z15.object({
1201
+ artifactId: z15.string(),
1202
+ slug: z15.string(),
1203
+ title: z15.string(),
1204
+ url: z15.string().url()
877
1205
  });
878
- var updateArtifactSchema = z13.object({
1206
+ var updateArtifactSchema = z15.object({
879
1207
  // Custom icon; explicit null clears it back to the default glyph.
880
1208
  // Part of what the artifact *is* (like the title), so it takes the
881
1209
  // same manage rights as title/slug — not the member-level folder move.
@@ -883,159 +1211,159 @@ var updateArtifactSchema = z13.object({
883
1211
  // SPEC-2 §6.4 — placement + visibility. `folderId: null` clears the
884
1212
  // folder; `workspaceId: null` moves the artifact out of its workspace
885
1213
  // (§5.5 ownership transfer).
886
- folderId: z13.string().min(1).nullable().optional(),
1214
+ folderId: z15.string().min(1).nullable().optional(),
887
1215
  // Keep the version list out of the review shell. Presentation only:
888
1216
  // versions stay immutable, and a `/vN/` URL still resolves.
889
- hideVersions: z13.boolean().optional(),
890
- slug: z13.string().min(3).max(63).regex(SLUG_PATTERN, {
1217
+ hideVersions: z15.boolean().optional(),
1218
+ slug: z15.string().min(3).max(63).regex(SLUG_PATTERN, {
891
1219
  message: "slug must be lowercase letters, digits, and hyphens, with no leading/trailing hyphen"
892
1220
  }).optional(),
893
- title: z13.string().min(1).max(200).optional(),
1221
+ title: z15.string().min(1).max(200).optional(),
894
1222
  visibility: artifactVisibilitySchema.optional(),
895
- workspaceId: z13.string().min(1).nullable().optional()
1223
+ workspaceId: z15.string().min(1).nullable().optional()
896
1224
  }).refine(
897
1225
  (body) => body.title !== void 0 || body.slug !== void 0 || body.visibility !== void 0 || body.folderId !== void 0 || body.workspaceId !== void 0 || body.emoji !== void 0 || body.hideVersions !== void 0,
898
1226
  {
899
1227
  message: "provide at least one of title, slug, visibility, folderId, workspaceId, emoji, hideVersions"
900
1228
  }
901
1229
  );
902
- var deleteArtifactResponseSchema = z13.object({
903
- deleted: z13.literal(true)
1230
+ var deleteArtifactResponseSchema = z15.object({
1231
+ deleted: z15.literal(true)
904
1232
  });
905
1233
 
906
1234
  // ../shared/src/presence.ts
907
- import { z as z14 } from "zod";
1235
+ import { z as z16 } from "zod";
908
1236
  var AGENT_STATES = ["listening", "editing", "publishing"];
909
- var agentStateSchema = z14.enum(AGENT_STATES);
1237
+ var agentStateSchema = z16.enum(AGENT_STATES);
910
1238
  var AGENT_PRESENCE = {
911
1239
  editingTtlSeconds: 300,
912
1240
  heartbeatSeconds: 15,
913
1241
  shellPollSeconds: 15,
914
1242
  ttlSeconds: 45
915
1243
  };
916
- var agentHeartbeatBodySchema = z14.object({
1244
+ var agentHeartbeatBodySchema = z16.object({
917
1245
  state: agentStateSchema.optional()
918
1246
  });
919
- var agentHeartbeatResponseSchema = z14.object({
920
- listening: z14.literal(true),
1247
+ var agentHeartbeatResponseSchema = z16.object({
1248
+ listening: z16.literal(true),
921
1249
  state: agentStateSchema.optional(),
922
- ttlSeconds: z14.number().int().positive()
1250
+ ttlSeconds: z16.number().int().positive()
923
1251
  });
924
- var agentPresenceSchema = z14.object({
925
- agent: z14.object({ id: z14.string().nullable(), label: z14.string() }).nullable().default(null),
926
- listening: z14.boolean(),
1252
+ var agentPresenceSchema = z16.object({
1253
+ agent: z16.object({ id: z16.string().nullable(), label: z16.string() }).nullable().default(null),
1254
+ listening: z16.boolean(),
927
1255
  state: agentStateSchema.nullable().optional(),
928
- versionNumber: z14.number().int().positive().nullable()
1256
+ versionNumber: z16.number().int().positive().nullable()
929
1257
  });
930
1258
 
931
1259
  // ../shared/src/protection.ts
932
- import { z as z15 } from "zod";
1260
+ import { z as z17 } from "zod";
933
1261
  var PASSWORD_MIN = 6;
934
1262
  var PASSWORD_MAX = 128;
935
- var gatePasswordSchema = z15.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
936
- var setProtectionSchema = z15.object({
1263
+ var gatePasswordSchema = z17.string().min(PASSWORD_MIN, `Password must be at least ${PASSWORD_MIN} characters.`).max(PASSWORD_MAX, `Password must be at most ${PASSWORD_MAX} characters.`);
1264
+ var setProtectionSchema = z17.object({
937
1265
  password: gatePasswordSchema.nullable()
938
1266
  });
939
- var protectionResponseSchema = z15.object({
940
- protected: z15.boolean()
1267
+ var protectionResponseSchema = z17.object({
1268
+ protected: z17.boolean()
941
1269
  });
942
- var unlockSchema = z15.object({
943
- password: z15.string().min(1).max(1024)
1270
+ var unlockSchema = z17.object({
1271
+ password: z17.string().min(1).max(1024)
944
1272
  });
945
- var unlockResponseSchema = z15.object({
946
- unlocked: z15.literal(true)
1273
+ var unlockResponseSchema = z17.object({
1274
+ unlocked: z17.literal(true)
947
1275
  });
948
1276
 
949
1277
  // ../shared/src/report.ts
950
- import { z as z16 } from "zod";
951
- var reportRequestSchema = z16.object({
952
- reason: z16.string().max(1e3).optional()
1278
+ import { z as z18 } from "zod";
1279
+ var reportRequestSchema = z18.object({
1280
+ reason: z18.string().max(1e3).optional()
953
1281
  });
954
- var reportResponseSchema = z16.object({
955
- reported: z16.literal(true)
1282
+ var reportResponseSchema = z18.object({
1283
+ reported: z18.literal(true)
956
1284
  });
957
1285
 
958
1286
  // ../shared/src/reviewers.ts
959
- import { z as z17 } from "zod";
960
- var artifactReviewerSchema = z17.object({
961
- avatarUrl: z17.string().nullable(),
962
- displayName: z17.string(),
1287
+ import { z as z19 } from "zod";
1288
+ var artifactReviewerSchema = z19.object({
1289
+ avatarUrl: z19.string().nullable(),
1290
+ displayName: z19.string(),
963
1291
  // Exactly one of userId/guestId is set, per `kind`.
964
- guestId: z17.string().nullable(),
1292
+ guestId: z19.string().nullable(),
965
1293
  // "account", not commentAuthorSchema's "member" — the whole point here is
966
1294
  // that these people need not be workspace members. A guest can be named in
967
1295
  // a comment but never notified: guest_identities carries a display name and
968
1296
  // no email, so there is nowhere to send anything.
969
- kind: z17.enum(["account", "guest"]),
970
- userId: z17.string().nullable()
1297
+ kind: z19.enum(["account", "guest"]),
1298
+ userId: z19.string().nullable()
971
1299
  });
972
- var artifactReviewerListResponseSchema = z17.object({
973
- reviewers: z17.array(artifactReviewerSchema)
1300
+ var artifactReviewerListResponseSchema = z19.object({
1301
+ reviewers: z19.array(artifactReviewerSchema)
974
1302
  });
975
1303
 
976
1304
  // ../shared/src/stats.ts
977
- import { z as z18 } from "zod";
978
- var avgViewSecondsSchema = z18.number().int().nonnegative().nullable();
979
- var versionViewStatsSchema = z18.object({
1305
+ import { z as z20 } from "zod";
1306
+ var avgViewSecondsSchema = z20.number().int().nonnegative().nullable();
1307
+ var versionViewStatsSchema = z20.object({
980
1308
  avgViewSeconds: avgViewSecondsSchema,
981
1309
  // Views from inside the review shell's iframe vs. direct visits to
982
1310
  // the render URL. views = embeddedViews + directViews.
983
- directViews: z18.number().int().nonnegative(),
984
- embeddedViews: z18.number().int().nonnegative(),
1311
+ directViews: z20.number().int().nonnegative(),
1312
+ embeddedViews: z20.number().int().nonnegative(),
985
1313
  // Distinct hashed viewer IPs across the version's lifetime.
986
- uniqueViewers: z18.number().int().nonnegative(),
987
- versionNumber: z18.number().int().positive(),
988
- views: z18.number().int().nonnegative()
1314
+ uniqueViewers: z20.number().int().nonnegative(),
1315
+ versionNumber: z20.number().int().positive(),
1316
+ views: z20.number().int().nonnegative()
989
1317
  });
990
- var dailyViewsSchema = z18.object({
1318
+ var dailyViewsSchema = z20.object({
991
1319
  avgViewSeconds: avgViewSecondsSchema,
992
1320
  // UTC day, YYYY-MM-DD.
993
- day: z18.string(),
994
- uniqueViewers: z18.number().int().nonnegative(),
995
- views: z18.number().int().nonnegative()
1321
+ day: z20.string(),
1322
+ uniqueViewers: z20.number().int().nonnegative(),
1323
+ views: z20.number().int().nonnegative()
996
1324
  });
997
- var artifactStatsResponseSchema = z18.object({
1325
+ var artifactStatsResponseSchema = z20.object({
998
1326
  avgViewSeconds: avgViewSecondsSchema,
999
- daily: z18.array(dailyViewsSchema),
1327
+ daily: z20.array(dailyViewsSchema),
1000
1328
  // Distinct hashed viewer IPs across all versions and days.
1001
- totalUniqueViewers: z18.number().int().nonnegative(),
1002
- totalViews: z18.number().int().nonnegative(),
1003
- versions: z18.array(versionViewStatsSchema)
1329
+ totalUniqueViewers: z20.number().int().nonnegative(),
1330
+ totalViews: z20.number().int().nonnegative(),
1331
+ versions: z20.array(versionViewStatsSchema)
1004
1332
  });
1005
1333
 
1006
1334
  // ../shared/src/versions.ts
1007
- import { z as z19 } from "zod";
1008
- var versionSourceSchema = z19.object({
1009
- agentName: z19.string().nullable(),
1010
- type: z19.string()
1335
+ import { z as z21 } from "zod";
1336
+ var versionSourceSchema = z21.object({
1337
+ agentName: z21.string().nullable(),
1338
+ type: z21.string()
1011
1339
  });
1012
- var versionChangesSchema = z19.object({
1013
- added: z19.array(z19.string()),
1014
- changed: z19.array(z19.string()),
1015
- removed: z19.array(z19.string())
1340
+ var versionChangesSchema = z21.object({
1341
+ added: z21.array(z21.string()),
1342
+ changed: z21.array(z21.string()),
1343
+ removed: z21.array(z21.string())
1016
1344
  });
1017
- var versionSummarySchema = z19.object({
1345
+ var versionSummarySchema = z21.object({
1018
1346
  // Absent for the first version — there is no previous version to diff against.
1019
1347
  changes: versionChangesSchema.nullable(),
1020
- createdAt: z19.string(),
1021
- entryUrl: z19.string().url(),
1022
- fileCount: z19.number().int().nonnegative(),
1023
- id: z19.string(),
1024
- isCurrent: z19.boolean(),
1348
+ createdAt: z21.string(),
1349
+ entryUrl: z21.string().url(),
1350
+ fileCount: z21.number().int().nonnegative(),
1351
+ id: z21.string(),
1352
+ isCurrent: z21.boolean(),
1025
1353
  // Author-supplied bullet-point changelog; null when the publish omitted it.
1026
- notes: z19.string().nullable(),
1354
+ notes: z21.string().nullable(),
1027
1355
  // The account that published this version, with their own avatar where
1028
1356
  // they've set one. Any workspace member can publish a new version
1029
1357
  // (SPEC-2 §5.1), so "which teammate" outranks "which agent" — the agent
1030
1358
  // is the tool, this is the person. Null for token-only publishes, which
1031
1359
  // carry no account.
1032
- publishedBy: z19.object({ image: z19.string().nullable(), name: z19.string() }).nullable(),
1360
+ publishedBy: z21.object({ image: z21.string().nullable(), name: z21.string() }).nullable(),
1033
1361
  source: versionSourceSchema,
1034
- totalSizeBytes: z19.number().int().nonnegative(),
1035
- versionNumber: z19.number().int().positive()
1362
+ totalSizeBytes: z21.number().int().nonnegative(),
1363
+ versionNumber: z21.number().int().positive()
1036
1364
  });
1037
- var versionListResponseSchema = z19.object({
1038
- versions: z19.array(versionSummarySchema)
1365
+ var versionListResponseSchema = z21.object({
1366
+ versions: z21.array(versionSummarySchema)
1039
1367
  });
1040
1368
 
1041
1369
  // src/agent-identity.ts
@@ -1120,6 +1448,13 @@ function createApiClient(apiUrl2, publishToken) {
1120
1448
  (d) => agentHeartbeatResponseSchema.parse(d)
1121
1449
  );
1122
1450
  },
1451
+ async clearData(artifactId, collection) {
1452
+ return request(
1453
+ `${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}`,
1454
+ { headers, method: "DELETE" },
1455
+ (d) => dataClearResponseSchema.parse(d)
1456
+ );
1457
+ },
1123
1458
  // SPEC-2 §6.3 — create a folder in a workspace. Account key required
1124
1459
  // (owner/admin; in a personal workspace that's always the user).
1125
1460
  async createFolder(workspaceId, body) {
@@ -1202,6 +1537,13 @@ function createApiClient(apiUrl2, publishToken) {
1202
1537
  (d) => artifactResponseSchema.parse(d)
1203
1538
  );
1204
1539
  },
1540
+ async getDataSummary(artifactId) {
1541
+ return request(
1542
+ `${base}/api/v1/artifacts/${artifactId}/data`,
1543
+ { headers, method: "GET" },
1544
+ (d) => dataSummaryResponseSchema.parse(d)
1545
+ );
1546
+ },
1205
1547
  // SPEC §6.18 — everything owned by the account this client's API
1206
1548
  // key (or session) belongs to.
1207
1549
  async listArtifacts() {
@@ -1211,6 +1553,21 @@ function createApiClient(apiUrl2, publishToken) {
1211
1553
  (d) => artifactListResponseSchema.parse(d)
1212
1554
  );
1213
1555
  },
1556
+ async listData(artifactId, collection, query = {}) {
1557
+ const params = new URLSearchParams();
1558
+ if (query.limit !== void 0) {
1559
+ params.set("limit", String(query.limit));
1560
+ }
1561
+ if (query.cursor !== void 0) {
1562
+ params.set("cursor", query.cursor);
1563
+ }
1564
+ const qs = params.toString();
1565
+ return request(
1566
+ `${base}/api/v1/artifacts/${artifactId}/data/${encodeURIComponent(collection)}${qs ? `?${qs}` : ""}`,
1567
+ { headers, method: "GET" },
1568
+ (d) => dataListResponseSchema.parse(d)
1569
+ );
1570
+ },
1214
1571
  async listFolders(workspaceId) {
1215
1572
  return request(
1216
1573
  `${base}/api/v1/workspaces/${workspaceId}/folders`,
@@ -1346,17 +1703,17 @@ import {
1346
1703
  writeFileSync as writeFileSync2
1347
1704
  } from "fs";
1348
1705
  import path2 from "path";
1349
- import { z as z21 } from "zod";
1706
+ import { z as z23 } from "zod";
1350
1707
 
1351
1708
  // src/credentials.ts
1352
1709
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
1353
1710
  import { homedir } from "os";
1354
1711
  import path from "path";
1355
- import { z as z20 } from "zod";
1712
+ import { z as z22 } from "zod";
1356
1713
  var DEFAULT_API_URL = "https://artifact.cafe";
1357
- var credentialsSchema = z20.object({
1358
- keys: z20.record(z20.string(), z20.string()),
1359
- version: z20.literal(1)
1714
+ var credentialsSchema = z22.object({
1715
+ keys: z22.record(z22.string(), z22.string()),
1716
+ version: z22.literal(1)
1360
1717
  });
1361
1718
  function credentialsPath() {
1362
1719
  return path.join(homedir(), ".artifactcafe", "credentials");
@@ -1406,24 +1763,24 @@ function resolveApiKey(apiUrl2, flag) {
1406
1763
  }
1407
1764
 
1408
1765
  // src/config.ts
1409
- var configSchema = z21.object({
1410
- apiUrl: z21.string(),
1411
- artifactId: z21.string(),
1412
- folder: z21.string().optional(),
1413
- folderId: z21.string().optional(),
1766
+ var configSchema = z23.object({
1767
+ apiUrl: z23.string(),
1768
+ artifactId: z23.string(),
1769
+ folder: z23.string().optional(),
1770
+ folderId: z23.string().optional(),
1414
1771
  // Optional: a folder linked by `link` to an artifact you own carries no
1415
1772
  // token — owner actions authorize via your stored login instead. A first
1416
1773
  // publish still writes the anonymous-mode token here.
1417
- publishToken: z21.string().optional(),
1418
- slug: z21.string(),
1419
- url: z21.string(),
1420
- workspace: z21.string().optional(),
1421
- workspaceId: z21.string().optional(),
1422
- workspaceKind: z21.enum(["personal", "workspace"]).optional()
1423
- });
1424
- var projectConfigSchema = z21.object({
1425
- folder: z21.string().trim().min(1).optional(),
1426
- workspace: z21.string().trim().min(1)
1774
+ publishToken: z23.string().optional(),
1775
+ slug: z23.string(),
1776
+ url: z23.string(),
1777
+ workspace: z23.string().optional(),
1778
+ workspaceId: z23.string().optional(),
1779
+ workspaceKind: z23.enum(["personal", "workspace"]).optional()
1780
+ });
1781
+ var projectConfigSchema = z23.object({
1782
+ folder: z23.string().trim().min(1).optional(),
1783
+ workspace: z23.string().trim().min(1)
1427
1784
  }).strict();
1428
1785
  var CONFIG_DIR = ".artifactcafe";
1429
1786
  var CONFIG_FILE = "config.json";
@@ -1459,7 +1816,7 @@ function readProjectConfig(dir) {
1459
1816
  JSON.parse(readFileSync2(file, "utf8"))
1460
1817
  );
1461
1818
  } catch (error) {
1462
- const message = error instanceof z21.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1819
+ const message = error instanceof z23.ZodError ? error.issues[0]?.message ?? "invalid project target" : "invalid JSON";
1463
1820
  throw new Error(`Invalid ${file}: ${message}.`);
1464
1821
  }
1465
1822
  }
@@ -1669,27 +2026,27 @@ async function comments(opts) {
1669
2026
  });
1670
2027
  return;
1671
2028
  }
1672
- const data = await client.exportComments(target.artifactId, {
2029
+ const data2 = await client.exportComments(target.artifactId, {
1673
2030
  status: status2,
1674
2031
  version
1675
2032
  });
1676
2033
  if (opts.json) {
1677
- console.log(JSON.stringify(data, null, 2));
2034
+ console.log(JSON.stringify(data2, null, 2));
1678
2035
  return;
1679
2036
  }
1680
- printHuman(data, url, status2, version);
2037
+ printHuman(data2, url, status2, version);
1681
2038
  }
1682
2039
  async function deriveReviewUrl(client, target) {
1683
2040
  const artifact = await client.getArtifact(target.artifactId);
1684
2041
  return reviewUrl(target.apiUrl, artifact.slug);
1685
2042
  }
1686
2043
  var sleep = (s) => new Promise((r) => setTimeout(r, s * 1e3));
1687
- function messageCounts(data) {
1688
- return new Map(data.comments.map((t) => [t.id, 1 + t.replies.length]));
2044
+ function messageCounts(data2) {
2045
+ return new Map(data2.comments.map((t) => [t.id, 1 + t.replies.length]));
1689
2046
  }
1690
- function diffThreads(baseline, data) {
2047
+ function diffThreads(baseline, data2) {
1691
2048
  const changed = [];
1692
- for (const t of data.comments) {
2049
+ for (const t of data2.comments) {
1693
2050
  const before = baseline.get(t.id);
1694
2051
  if (before === void 0) {
1695
2052
  changed.push({ ...t, change: "new" });
@@ -1732,8 +2089,8 @@ async function waitForFeedback(client, config, opts) {
1732
2089
  while (Date.now() < deadline) {
1733
2090
  await sleep(Math.min(pollSeconds(), (deadline - Date.now()) / 1e3));
1734
2091
  await heartbeat();
1735
- const data = await client.exportComments(config.artifactId, query);
1736
- if (diffThreads(baseline, data).length === 0) {
2092
+ const data2 = await client.exportComments(config.artifactId, query);
2093
+ if (diffThreads(baseline, data2).length === 0) {
1737
2094
  continue;
1738
2095
  }
1739
2096
  await sleep(settleSeconds());
@@ -1766,7 +2123,7 @@ async function waitForFeedback(client, config, opts) {
1766
2123
  }
1767
2124
  process.exit(WAIT_TIMEOUT_EXIT);
1768
2125
  }
1769
- function printWaitResult(config, data, changed, json) {
2126
+ function printWaitResult(config, data2, changed, json) {
1770
2127
  if (json) {
1771
2128
  console.log(
1772
2129
  JSON.stringify(
@@ -1774,7 +2131,7 @@ function printWaitResult(config, data, changed, json) {
1774
2131
  artifactId: config.artifactId,
1775
2132
  threads: changed,
1776
2133
  timedOut: false,
1777
- versionNumber: data.versionNumber
2134
+ versionNumber: data2.versionNumber
1778
2135
  },
1779
2136
  null,
1780
2137
  2
@@ -1793,22 +2150,22 @@ ${changed.length} thread${plural} with new feedback \u2014 ${config.url}
1793
2150
  printThread(thread);
1794
2151
  }
1795
2152
  }
1796
- function printHuman(data, url, status2, version) {
2153
+ function printHuman(data2, url, status2, version) {
1797
2154
  const statusLabel = status2 === "all" ? "" : `${status2} `;
1798
2155
  let scope = "across all versions";
1799
2156
  if (version === "current") {
1800
- scope = data.versionNumber === null ? "on the current version" : `on v${data.versionNumber}`;
2157
+ scope = data2.versionNumber === null ? "on the current version" : `on v${data2.versionNumber}`;
1801
2158
  }
1802
- if (data.comments.length === 0) {
2159
+ if (data2.comments.length === 0) {
1803
2160
  console.log(`No ${statusLabel}comments ${scope}.`);
1804
2161
  return;
1805
2162
  }
1806
- const plural = data.comments.length === 1 ? "" : "s";
2163
+ const plural = data2.comments.length === 1 ? "" : "s";
1807
2164
  console.log(
1808
- `${data.comments.length} ${statusLabel}thread${plural} ${scope} \u2014 ${url}
2165
+ `${data2.comments.length} ${statusLabel}thread${plural} ${scope} \u2014 ${url}
1809
2166
  `
1810
2167
  );
1811
- for (const thread of data.comments) {
2168
+ for (const thread of data2.comments) {
1812
2169
  printThread(thread);
1813
2170
  }
1814
2171
  }
@@ -1853,18 +2210,161 @@ function indent(text, pad) {
1853
2210
  }
1854
2211
 
1855
2212
  // src/dashboard.ts
1856
- import path7 from "path";
2213
+ import path8 from "path";
1857
2214
 
1858
2215
  // src/publish.ts
1859
2216
  import { exec } from "child_process";
1860
- import { readFileSync as readFileSync4 } from "fs";
1861
- import path6 from "path";
2217
+ import { readFileSync as readFileSync5 } from "fs";
2218
+ import path7 from "path";
1862
2219
  import { createInterface } from "readline";
1863
2220
 
2221
+ // src/data.ts
2222
+ import { readFileSync as readFileSync3 } from "fs";
2223
+ import path5 from "path";
2224
+ function inspectDataManifest(files) {
2225
+ const info = { announcements: [] };
2226
+ const manifestFile = files.find((f) => f.path === DATA_MANIFEST_PATH);
2227
+ if (manifestFile) {
2228
+ const parsed = parseDataManifest(
2229
+ readFileSync3(manifestFile.absPath, "utf8")
2230
+ );
2231
+ if (!parsed.ok) {
2232
+ throw new Error(
2233
+ `${DATA_MANIFEST_PATH} is invalid:
2234
+ ${(parsed.errors ?? []).map((e) => ` - ${e}`).join("\n")}`
2235
+ );
2236
+ }
2237
+ info.config = parsed.config;
2238
+ for (const [name, collection] of Object.entries(
2239
+ parsed.config?.collections ?? {}
2240
+ )) {
2241
+ const bits = [
2242
+ `read: ${collection.access.read}`,
2243
+ `insert: ${collection.access.insert}`
2244
+ ];
2245
+ if (collection.access.update !== "owner") {
2246
+ bits.push(`update: ${collection.access.update}`);
2247
+ }
2248
+ if (collection.access.delete !== "owner") {
2249
+ bits.push(`delete: ${collection.access.delete}`);
2250
+ }
2251
+ if (collection.rateLimit) {
2252
+ bits.push(collection.rateLimit);
2253
+ }
2254
+ info.announcements.push(`\u26C1 Data enabled: ${name} (${bits.join(", ")})`);
2255
+ }
2256
+ if (info.announcements.length === 0) {
2257
+ info.announcements.push(
2258
+ "\u26C1 Data disabled (empty collections in .artifact/data.json)"
2259
+ );
2260
+ }
2261
+ }
2262
+ return info;
2263
+ }
2264
+ function requireLinkedArtifact(dir) {
2265
+ const resolved = path5.resolve(dir);
2266
+ const config = readConfig(resolved);
2267
+ if (!config) {
2268
+ throw new Error(
2269
+ `No artifact linked in ${resolved} \u2014 run \`artifact-cafe publish\` there first (.artifactcafe/config.json not found).`
2270
+ );
2271
+ }
2272
+ return {
2273
+ apiUrl: process.env.ARTIFACT_CAFE_URL ?? config.apiUrl,
2274
+ artifactId: config.artifactId,
2275
+ publishToken: resolveApiKey(process.env.ARTIFACT_CAFE_URL ?? config.apiUrl) ?? config.publishToken ?? process.env.ARTIFACT_CAFE_TOKEN,
2276
+ url: config.url
2277
+ };
2278
+ }
2279
+ async function dataLs(opts) {
2280
+ const linked = requireLinkedArtifact(opts.dir);
2281
+ const client = createApiClient(linked.apiUrl, linked.publishToken);
2282
+ const records = [];
2283
+ let cursor;
2284
+ do {
2285
+ const page = await client.listData(linked.artifactId, opts.collection, {
2286
+ cursor,
2287
+ limit: 200
2288
+ });
2289
+ records.push(...page.records);
2290
+ cursor = page.nextCursor ?? void 0;
2291
+ } while (cursor && (opts.limit === void 0 || records.length < opts.limit));
2292
+ const shown = opts.limit === void 0 ? records : records.slice(0, opts.limit);
2293
+ if (opts.json) {
2294
+ console.log(
2295
+ JSON.stringify({ collection: opts.collection, records: shown }, null, 2)
2296
+ );
2297
+ return;
2298
+ }
2299
+ if (shown.length === 0) {
2300
+ console.log(`No records in "${opts.collection}".`);
2301
+ return;
2302
+ }
2303
+ const plural = shown.length === 1 ? "" : "s";
2304
+ console.log(
2305
+ `${shown.length} record${plural} in "${opts.collection}" \u2014 ${linked.url}
2306
+ `
2307
+ );
2308
+ for (const record of shown) {
2309
+ const { created_at, created_by, id, updated_at: _u, ...fields } = record;
2310
+ const author = created_by ?? "anonymous";
2311
+ console.log(`\u25CF ${id} \xB7 ${String(created_at).slice(0, 10)} \xB7 ${author}`);
2312
+ console.log(` ${JSON.stringify(fields)}`);
2313
+ }
2314
+ }
2315
+ async function dataExport(opts) {
2316
+ const linked = requireLinkedArtifact(opts.dir);
2317
+ const client = createApiClient(linked.apiUrl, linked.publishToken);
2318
+ const summary = await client.getDataSummary(linked.artifactId);
2319
+ const collections = {};
2320
+ for (const { name } of summary.collections) {
2321
+ const records = [];
2322
+ let cursor;
2323
+ do {
2324
+ const page = await client.listData(linked.artifactId, name, {
2325
+ cursor,
2326
+ limit: 200
2327
+ });
2328
+ records.push(...page.records);
2329
+ cursor = page.nextCursor ?? void 0;
2330
+ } while (cursor);
2331
+ collections[name] = records;
2332
+ }
2333
+ console.log(
2334
+ JSON.stringify(
2335
+ {
2336
+ artifactId: linked.artifactId,
2337
+ collections,
2338
+ enabled: summary.enabled,
2339
+ url: linked.url
2340
+ },
2341
+ null,
2342
+ 2
2343
+ )
2344
+ );
2345
+ }
2346
+ async function dataClear(opts) {
2347
+ if (!opts.yes) {
2348
+ throw new Error(
2349
+ `Clearing "${opts.collection}" deletes every record in it. Re-run with --yes to confirm.`
2350
+ );
2351
+ }
2352
+ const linked = requireLinkedArtifact(opts.dir);
2353
+ const client = createApiClient(linked.apiUrl, linked.publishToken);
2354
+ const result = await client.clearData(linked.artifactId, opts.collection);
2355
+ if (opts.json) {
2356
+ console.log(
2357
+ JSON.stringify({ cleared: result.cleared, collection: opts.collection })
2358
+ );
2359
+ return;
2360
+ }
2361
+ console.log(`Cleared ${result.cleared} record(s) from "${opts.collection}".`);
2362
+ }
2363
+
1864
2364
  // src/manifest.ts
1865
2365
  import { createHash } from "crypto";
1866
- import { readdirSync, readFileSync as readFileSync3, statSync } from "fs";
1867
- import path5 from "path";
2366
+ import { readdirSync, readFileSync as readFileSync4, statSync } from "fs";
2367
+ import path6 from "path";
1868
2368
  var IGNORED_DIRS = /* @__PURE__ */ new Set([".git", "node_modules", ".artifactcafe"]);
1869
2369
  var IGNORED_FILES = /* @__PURE__ */ new Set([".DS_Store", "artifact-cafe.json"]);
1870
2370
  function isIgnoredFile(name) {
@@ -1913,7 +2413,7 @@ var MIME_TYPES = {
1913
2413
  ".xml": "application/xml"
1914
2414
  };
1915
2415
  function mimeFor(filePath) {
1916
- return MIME_TYPES[path5.extname(filePath).toLowerCase()] ?? "application/octet-stream";
2416
+ return MIME_TYPES[path6.extname(filePath).toLowerCase()] ?? "application/octet-stream";
1917
2417
  }
1918
2418
  function scanDirectory(dir) {
1919
2419
  const files = [];
@@ -1921,7 +2421,7 @@ function scanDirectory(dir) {
1921
2421
  function walk(current, relPrefix) {
1922
2422
  for (const entry of readdirSync(current, { withFileTypes: true })) {
1923
2423
  const rel = relPrefix ? `${relPrefix}/${entry.name}` : entry.name;
1924
- const abs = path5.join(current, entry.name);
2424
+ const abs = path6.join(current, entry.name);
1925
2425
  if (entry.isSymbolicLink()) {
1926
2426
  skipped.push(rel);
1927
2427
  continue;
@@ -1946,7 +2446,7 @@ function scanDirectory(dir) {
1946
2446
  skipped.push(rel);
1947
2447
  continue;
1948
2448
  }
1949
- const content = readFileSync3(abs);
2449
+ const content = readFileSync4(abs);
1950
2450
  files.push({
1951
2451
  absPath: abs,
1952
2452
  hash: `sha256_${createHash("sha256").update(content).digest("hex")}`,
@@ -1992,7 +2492,7 @@ var REVIEW_LABEL = {
1992
2492
  readonly: "read-only \u2014 existing comments only"
1993
2493
  };
1994
2494
  async function publish(opts) {
1995
- const dir = path6.resolve(opts.dir);
2495
+ const dir = path7.resolve(opts.dir);
1996
2496
  const config = readConfig(dir);
1997
2497
  if (opts.password !== void 0) {
1998
2498
  const parsed = gatePasswordSchema.safeParse(opts.password);
@@ -2025,6 +2525,7 @@ async function publish(opts) {
2025
2525
  throw new Error(`Nothing to publish in ${dir} (after ignore rules).`);
2026
2526
  }
2027
2527
  const entryPath = resolveEntry(files, opts.entry);
2528
+ const dataInfo = inspectDataManifest(files);
2028
2529
  const log = (msg) => {
2029
2530
  if (!opts.json) {
2030
2531
  console.log(msg);
@@ -2085,7 +2586,7 @@ async function publish(opts) {
2085
2586
  }
2086
2587
  await uploadFile(
2087
2588
  upload.uploadUrl,
2088
- readFileSync4(file.absPath),
2589
+ readFileSync5(file.absPath),
2089
2590
  file.mimeType
2090
2591
  );
2091
2592
  }
@@ -2133,6 +2634,7 @@ async function publish(opts) {
2133
2634
  JSON.stringify(
2134
2635
  {
2135
2636
  artifactId: result.artifactId,
2637
+ ...dataInfo.config ? { data: dataInfo.config } : {},
2136
2638
  claimed: result.claimed,
2137
2639
  destination: placement.destination,
2138
2640
  destinationSource: placement.destinationSource,
@@ -2211,6 +2713,11 @@ ${result.claimUrl}
2211
2713
  `);
2212
2714
  }
2213
2715
  }
2716
+ if (!opts.json) {
2717
+ for (const announcement of dataInfo.announcements) {
2718
+ console.log(announcement);
2719
+ }
2720
+ }
2214
2721
  if (opts.open && !opts.json && process.stdout.isTTY) {
2215
2722
  openInBrowser(result.url);
2216
2723
  }
@@ -2280,9 +2787,9 @@ async function resolvePlacement(params) {
2280
2787
  };
2281
2788
  }
2282
2789
  if (shouldPromptForWorkspace(opts)) {
2283
- const data2 = await createApiClient(apiUrl2, apiKey).listWorkspaces();
2284
- if (data2.workspaces.length > 1) {
2285
- const choice = await pickWorkspace(data2.workspaces);
2790
+ const data3 = await createApiClient(apiUrl2, apiKey).listWorkspaces();
2791
+ if (data3.workspaces.length > 1) {
2792
+ const choice = await pickWorkspace(data3.workspaces);
2286
2793
  return {
2287
2794
  destination: choice.workspace.isPersonal ? "personal" : "workspace",
2288
2795
  destinationLabel: choice.workspace.isPersonal ? "Personal" : choice.workspace.name,
@@ -2308,19 +2815,19 @@ async function resolvePlacement(params) {
2308
2815
  throw new Error("--folder requires --workspace or a stored workspace.");
2309
2816
  }
2310
2817
  const client = createApiClient(apiUrl2, apiKey);
2311
- const data = await client.listWorkspaces();
2818
+ const data2 = await client.listWorkspaces();
2312
2819
  const workspaceHandle = opts.workspace ?? config?.workspace ?? projectConfig?.workspace;
2313
2820
  let workspace;
2314
2821
  if (workspaceHandle) {
2315
- workspace = data.workspaces.find(
2822
+ workspace = data2.workspaces.find(
2316
2823
  (candidate) => candidate.handle === workspaceHandle
2317
2824
  );
2318
2825
  } else if (config?.workspaceId) {
2319
- workspace = data.workspaces.find(
2826
+ workspace = data2.workspaces.find(
2320
2827
  (candidate) => candidate.id === config.workspaceId
2321
2828
  );
2322
2829
  } else {
2323
- workspace = data.workspaces.find((candidate) => candidate.isPersonal);
2830
+ workspace = data2.workspaces.find((candidate) => candidate.isPersonal);
2324
2831
  }
2325
2832
  if (!workspace) {
2326
2833
  const target = workspaceHandle ? `workspace with handle "${workspaceHandle}"` : "personal workspace";
@@ -2458,7 +2965,7 @@ function openInBrowser(url) {
2458
2965
 
2459
2966
  // src/dashboard.ts
2460
2967
  function openDashboard(opts) {
2461
- const base = process.env.ARTIFACT_CAFE_URL ?? readConfig(path7.resolve(opts.dir))?.apiUrl ?? DEFAULT_API_URL;
2968
+ const base = process.env.ARTIFACT_CAFE_URL ?? readConfig(path8.resolve(opts.dir))?.apiUrl ?? DEFAULT_API_URL;
2462
2969
  const url = `${base.replace(/\/$/, "")}/dashboard`;
2463
2970
  console.log(url);
2464
2971
  if (process.stdout.isTTY) {
@@ -2544,21 +3051,21 @@ async function folders(opts) {
2544
3051
  await createFolder(client, workspace, opts);
2545
3052
  return;
2546
3053
  }
2547
- const data = await client.listFolders(workspace.id);
3054
+ const data2 = await client.listFolders(workspace.id);
2548
3055
  if (opts.json) {
2549
- console.log(JSON.stringify(data, null, 2));
3056
+ console.log(JSON.stringify(data2, null, 2));
2550
3057
  return;
2551
3058
  }
2552
- if (data.folders.length === 0) {
3059
+ if (data2.folders.length === 0) {
2553
3060
  console.log(`No folders in workspace ${workspace.handle}.`);
2554
3061
  return;
2555
3062
  }
2556
- const plural = data.folders.length === 1 ? "" : "s";
3063
+ const plural = data2.folders.length === 1 ? "" : "s";
2557
3064
  console.log(
2558
- `${data.folders.length} folder${plural} in ${workspace.handle}
3065
+ `${data2.folders.length} folder${plural} in ${workspace.handle}
2559
3066
  `
2560
3067
  );
2561
- for (const folder of data.folders) {
3068
+ for (const folder of data2.folders) {
2562
3069
  printFolder(folder);
2563
3070
  }
2564
3071
  }
@@ -2596,7 +3103,7 @@ function printFolder(folder) {
2596
3103
  }
2597
3104
 
2598
3105
  // src/link.ts
2599
- import path8 from "path";
3106
+ import path9 from "path";
2600
3107
  import { createInterface as createInterface2 } from "readline";
2601
3108
 
2602
3109
  // src/list.ts
@@ -2653,28 +3160,28 @@ async function list(opts) {
2653
3160
  await listWorkspace(client, opts);
2654
3161
  return;
2655
3162
  }
2656
- const [data, groups] = await Promise.all([
3163
+ const [data2, groups] = await Promise.all([
2657
3164
  client.listArtifacts(),
2658
3165
  teamGroups(client)
2659
3166
  ]);
2660
3167
  if (opts.json) {
2661
3168
  console.log(
2662
- JSON.stringify({ artifacts: data.artifacts, workspaces: groups }, null, 2)
3169
+ JSON.stringify({ artifacts: data2.artifacts, workspaces: groups }, null, 2)
2663
3170
  );
2664
3171
  return;
2665
3172
  }
2666
- const total = data.artifacts.length + groups.reduce((sum, group) => sum + group.artifacts.length, 0);
3173
+ const total = data2.artifacts.length + groups.reduce((sum, group) => sum + group.artifacts.length, 0);
2667
3174
  if (total === 0) {
2668
3175
  console.log(
2669
3176
  "No artifacts you can publish to yet \u2014 publish while logged in, or claim one via its claim link."
2670
3177
  );
2671
3178
  return;
2672
3179
  }
2673
- if (data.artifacts.length > 0) {
2674
- const plural = data.artifacts.length === 1 ? "" : "s";
2675
- console.log(`${data.artifacts.length} artifact${plural}
3180
+ if (data2.artifacts.length > 0) {
3181
+ const plural = data2.artifacts.length === 1 ? "" : "s";
3182
+ console.log(`${data2.artifacts.length} artifact${plural}
2676
3183
  `);
2677
- for (const artifact of data.artifacts) {
3184
+ for (const artifact of data2.artifacts) {
2678
3185
  printArtifact(artifact);
2679
3186
  }
2680
3187
  }
@@ -2825,7 +3332,7 @@ async function link(opts) {
2825
3332
  }
2826
3333
  chosen = await pickArtifact(artifacts);
2827
3334
  }
2828
- const dir = path8.resolve(opts.dir);
3335
+ const dir = path9.resolve(opts.dir);
2829
3336
  writeConfig(dir, {
2830
3337
  apiUrl: apiUrl2,
2831
3338
  artifactId: chosen.id,
@@ -2993,7 +3500,7 @@ function logout(opts) {
2993
3500
  }
2994
3501
 
2995
3502
  // src/message.ts
2996
- import path9 from "path";
3503
+ import path10 from "path";
2997
3504
  async function readStdin2() {
2998
3505
  if (process.stdin.isTTY) {
2999
3506
  return "";
@@ -3017,7 +3524,7 @@ async function editMessage(opts) {
3017
3524
  'Nothing to save. Pass the new text with --body "\u2026", or pipe it on stdin.'
3018
3525
  );
3019
3526
  }
3020
- const dir = path9.resolve(opts.dir);
3527
+ const dir = path10.resolve(opts.dir);
3021
3528
  const target = resolveArtifactTarget(dir, {
3022
3529
  apiKey: opts.apiKey,
3023
3530
  artifact: opts.artifact
@@ -3037,7 +3544,7 @@ async function deleteMessage(opts) {
3037
3544
  "Pass the message id to delete \u2014 get it from `artifact-cafe comments --json`."
3038
3545
  );
3039
3546
  }
3040
- const dir = path9.resolve(opts.dir);
3547
+ const dir = path10.resolve(opts.dir);
3041
3548
  const target = resolveArtifactTarget(dir, {
3042
3549
  apiKey: opts.apiKey,
3043
3550
  artifact: opts.artifact
@@ -3052,12 +3559,12 @@ async function deleteMessage(opts) {
3052
3559
  }
3053
3560
 
3054
3561
  // src/move.ts
3055
- import path10 from "path";
3562
+ import path11 from "path";
3056
3563
  async function move(opts) {
3057
3564
  if (Boolean(opts.folder) === Boolean(opts.unfiled)) {
3058
3565
  throw new Error("Pass exactly one of --folder <name-or-id> or --unfiled.");
3059
3566
  }
3060
- const dir = path10.resolve(opts.dir);
3567
+ const dir = path11.resolve(opts.dir);
3061
3568
  const target = resolveArtifactTarget(dir, {
3062
3569
  apiKey: opts.apiKey,
3063
3570
  artifact: opts.artifact
@@ -3078,8 +3585,8 @@ async function move(opts) {
3078
3585
  let folderId = null;
3079
3586
  let folderName = null;
3080
3587
  if (opts.folder) {
3081
- const data = await client.listFolders(artifact.workspaceId);
3082
- const matches = data.folders.filter(
3588
+ const data2 = await client.listFolders(artifact.workspaceId);
3589
+ const matches = data2.folders.filter(
3083
3590
  (candidate) => candidate.name === opts.folder || candidate.id === opts.folder
3084
3591
  );
3085
3592
  if (matches.length === 0) {
@@ -3147,9 +3654,9 @@ async function move(opts) {
3147
3654
  }
3148
3655
 
3149
3656
  // src/open.ts
3150
- import path11 from "path";
3657
+ import path12 from "path";
3151
3658
  async function openArtifact(opts) {
3152
- const dir = path11.resolve(opts.dir);
3659
+ const dir = path12.resolve(opts.dir);
3153
3660
  const target = resolveArtifactTarget(dir, {
3154
3661
  apiKey: opts.apiKey,
3155
3662
  artifact: opts.artifact
@@ -3167,7 +3674,7 @@ async function openArtifact(opts) {
3167
3674
  }
3168
3675
 
3169
3676
  // src/protect.ts
3170
- import path12 from "path";
3677
+ import path13 from "path";
3171
3678
  async function protect(opts) {
3172
3679
  if (opts.off && opts.password !== void 0) {
3173
3680
  throw new Error("Pass either --password or --off, not both.");
@@ -3184,7 +3691,7 @@ async function protect(opts) {
3184
3691
  throw new Error(parsed.error.issues[0]?.message ?? "Invalid password.");
3185
3692
  }
3186
3693
  }
3187
- const dir = path12.resolve(opts.dir);
3694
+ const dir = path13.resolve(opts.dir);
3188
3695
  const target = resolveArtifactTarget(dir, {
3189
3696
  apiKey: opts.apiKey,
3190
3697
  artifact: opts.artifact
@@ -3209,7 +3716,7 @@ async function protect(opts) {
3209
3716
  }
3210
3717
 
3211
3718
  // src/reply.ts
3212
- import path13 from "path";
3719
+ import path14 from "path";
3213
3720
  async function readStdin3() {
3214
3721
  if (process.stdin.isTTY) {
3215
3722
  return "";
@@ -3233,7 +3740,7 @@ async function replyToThread(opts) {
3233
3740
  'Nothing to post. Pass the reply with --body "\u2026", or pipe it on stdin.'
3234
3741
  );
3235
3742
  }
3236
- const dir = path13.resolve(opts.dir);
3743
+ const dir = path14.resolve(opts.dir);
3237
3744
  const target = resolveArtifactTarget(dir, {
3238
3745
  apiKey: opts.apiKey,
3239
3746
  artifact: opts.artifact
@@ -3262,7 +3769,7 @@ async function replyToThread(opts) {
3262
3769
  }
3263
3770
 
3264
3771
  // src/resolve.ts
3265
- import path14 from "path";
3772
+ import path15 from "path";
3266
3773
  async function changeThreadStatus(opts) {
3267
3774
  const threadId = opts.thread?.trim();
3268
3775
  if (!threadId) {
@@ -3270,7 +3777,7 @@ async function changeThreadStatus(opts) {
3270
3777
  `Pass the thread id to ${opts.action} \u2014 get it from \`artifact-cafe comments --json\`.`
3271
3778
  );
3272
3779
  }
3273
- const dir = path14.resolve(opts.dir);
3780
+ const dir = path15.resolve(opts.dir);
3274
3781
  const target = resolveArtifactTarget(dir, {
3275
3782
  apiKey: opts.apiKey,
3276
3783
  artifact: opts.artifact
@@ -3307,7 +3814,7 @@ async function changeThreadStatus(opts) {
3307
3814
  }
3308
3815
 
3309
3816
  // src/review.ts
3310
- import path15 from "path";
3817
+ import path16 from "path";
3311
3818
  var OUTCOME = {
3312
3819
  off: "Plain artifact \u2014 the review tools are hidden entirely",
3313
3820
  open: "Open for review \u2014 anyone who can open it can comment",
@@ -3321,7 +3828,7 @@ async function review(opts) {
3321
3828
  );
3322
3829
  }
3323
3830
  const mode = parsed.data;
3324
- const dir = path15.resolve(opts.dir);
3831
+ const dir = path16.resolve(opts.dir);
3325
3832
  const target = resolveArtifactTarget(dir, {
3326
3833
  apiKey: opts.apiKey,
3327
3834
  artifact: opts.artifact
@@ -3343,10 +3850,10 @@ async function review(opts) {
3343
3850
  }
3344
3851
 
3345
3852
  // src/share.ts
3346
- import path16 from "path";
3853
+ import path17 from "path";
3347
3854
  var EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3348
3855
  async function share(opts) {
3349
- const dir = path16.resolve(opts.dir);
3856
+ const dir = path17.resolve(opts.dir);
3350
3857
  const target = resolveArtifactTarget(dir, {
3351
3858
  apiKey: opts.apiKey,
3352
3859
  artifact: opts.artifact
@@ -3401,7 +3908,7 @@ General access: ${visibility}`);
3401
3908
  console.log("");
3402
3909
  }
3403
3910
  async function unshare(opts) {
3404
- const dir = path16.resolve(opts.dir);
3911
+ const dir = path17.resolve(opts.dir);
3405
3912
  const target = resolveArtifactTarget(dir, {
3406
3913
  apiKey: opts.apiKey,
3407
3914
  artifact: opts.artifact
@@ -3424,7 +3931,7 @@ async function unshare(opts) {
3424
3931
  }
3425
3932
 
3426
3933
  // src/status.ts
3427
- import path17 from "path";
3934
+ import path18 from "path";
3428
3935
  async function status(opts) {
3429
3936
  if (!AGENT_STATES.includes(opts.state)) {
3430
3937
  throw new Error(
@@ -3432,7 +3939,7 @@ async function status(opts) {
3432
3939
  );
3433
3940
  }
3434
3941
  const state = opts.state;
3435
- const dir = path17.resolve(opts.dir);
3942
+ const dir = path18.resolve(opts.dir);
3436
3943
  const target = resolveArtifactTarget(dir, {
3437
3944
  apiKey: opts.apiKey,
3438
3945
  artifact: opts.artifact
@@ -3458,40 +3965,40 @@ async function status(opts) {
3458
3965
  }
3459
3966
 
3460
3967
  // src/update.ts
3461
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync3 } from "fs";
3968
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "fs";
3462
3969
  import { createRequire } from "module";
3463
3970
  import { homedir as homedir2 } from "os";
3464
- import path18 from "path";
3465
- import { z as z22 } from "zod";
3971
+ import path19 from "path";
3972
+ import { z as z24 } from "zod";
3466
3973
  var REGISTRY_LATEST_URL = "https://registry.npmjs.org/artifact-cafe/latest";
3467
3974
  var SKILL_SOURCE_URL = "https://raw.githubusercontent.com/Artifact-Cafe/skill/main/artifact-cafe/SKILL.md";
3468
3975
  var SKILL_UPDATE_COMMAND = "npx skills add artifact-cafe/skill --skill artifact-cafe -g";
3469
3976
  var CHECK_TTL_MS = 24 * 60 * 60 * 1e3;
3470
3977
  var FETCH_TIMEOUT_MS = 2e3;
3471
3978
  var SKILL_VERSION_PATTERN = /\*\*Skill version:\s*(\d+\.\d+\.\d+)\*\*/;
3472
- var cacheSchema = z22.object({
3473
- checkedAt: z22.number(),
3474
- cliLatest: z22.string().nullable(),
3475
- skillLatest: z22.string().nullable(),
3476
- version: z22.literal(1)
3979
+ var cacheSchema = z24.object({
3980
+ checkedAt: z24.number(),
3981
+ cliLatest: z24.string().nullable(),
3982
+ skillLatest: z24.string().nullable(),
3983
+ version: z24.literal(1)
3477
3984
  });
3478
3985
  function cliVersion() {
3479
3986
  const pkg = createRequire(import.meta.url)("../package.json");
3480
3987
  return pkg.version;
3481
3988
  }
3482
3989
  function cachePath() {
3483
- return path18.join(homedir2(), ".artifactcafe", "update-check.json");
3990
+ return path19.join(homedir2(), ".artifactcafe", "update-check.json");
3484
3991
  }
3485
3992
  function readCache() {
3486
3993
  try {
3487
- return cacheSchema.parse(JSON.parse(readFileSync5(cachePath(), "utf8")));
3994
+ return cacheSchema.parse(JSON.parse(readFileSync6(cachePath(), "utf8")));
3488
3995
  } catch {
3489
3996
  return null;
3490
3997
  }
3491
3998
  }
3492
3999
  function writeCache(cache) {
3493
4000
  try {
3494
- mkdirSync3(path18.dirname(cachePath()), { mode: 448, recursive: true });
4001
+ mkdirSync3(path19.dirname(cachePath()), { mode: 448, recursive: true });
3495
4002
  writeFileSync3(cachePath(), `${JSON.stringify(cache, null, 2)}
3496
4003
  `);
3497
4004
  } catch {
@@ -3528,7 +4035,7 @@ async function fetchText(url) {
3528
4035
  }
3529
4036
  async function fetchLatestVersions(previous) {
3530
4037
  const [cli, skill] = await Promise.all([
3531
- fetchJson(REGISTRY_LATEST_URL).then((body) => z22.object({ version: z22.string() }).parse(body).version).catch(() => null),
4038
+ fetchJson(REGISTRY_LATEST_URL).then((body) => z24.object({ version: z24.string() }).parse(body).version).catch(() => null),
3532
4039
  fetchText(SKILL_SOURCE_URL).then((body) => SKILL_VERSION_PATTERN.exec(body)?.[1] ?? null).catch(() => null)
3533
4040
  ]);
3534
4041
  return {
@@ -3544,12 +4051,12 @@ function installedSkillVersions() {
3544
4051
  const versions = [];
3545
4052
  for (const root of roots) {
3546
4053
  for (const dir of dirs) {
3547
- const file = path18.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
4054
+ const file = path19.join(root, dir, "skills", "artifact-cafe", "SKILL.md");
3548
4055
  if (!existsSync3(file)) {
3549
4056
  continue;
3550
4057
  }
3551
4058
  try {
3552
- const found = SKILL_VERSION_PATTERN.exec(readFileSync5(file, "utf8"));
4059
+ const found = SKILL_VERSION_PATTERN.exec(readFileSync6(file, "utf8"));
3553
4060
  if (found?.[1]) {
3554
4061
  versions.push(found[1]);
3555
4062
  }
@@ -3618,19 +4125,19 @@ async function workspaces(opts) {
3618
4125
  `Listing workspaces needs an account \u2014 run \`artifact-cafe login\` first (no API key for ${apiOrigin(apiUrl2)}).`
3619
4126
  );
3620
4127
  }
3621
- const data = await createApiClient(apiUrl2, key).listWorkspaces();
4128
+ const data2 = await createApiClient(apiUrl2, key).listWorkspaces();
3622
4129
  if (opts.json) {
3623
- console.log(JSON.stringify(data, null, 2));
4130
+ console.log(JSON.stringify(data2, null, 2));
3624
4131
  return;
3625
4132
  }
3626
- if (data.workspaces.length === 0) {
4133
+ if (data2.workspaces.length === 0) {
3627
4134
  console.log("No workspaces yet \u2014 create one from the dashboard.");
3628
4135
  return;
3629
4136
  }
3630
- const plural = data.workspaces.length === 1 ? "" : "s";
3631
- console.log(`${data.workspaces.length} workspace${plural}
4137
+ const plural = data2.workspaces.length === 1 ? "" : "s";
4138
+ console.log(`${data2.workspaces.length} workspace${plural}
3632
4139
  `);
3633
- for (const workspace of data.workspaces) {
4140
+ for (const workspace of data2.workspaces) {
3634
4141
  printWorkspace(workspace);
3635
4142
  }
3636
4143
  }
@@ -4599,6 +5106,110 @@ var dashboard = defineCommand({
4599
5106
  }
4600
5107
  }
4601
5108
  });
5109
+ var dataLsCommand = defineCommand({
5110
+ args: {
5111
+ collection: {
5112
+ description: "Collection name",
5113
+ required: true,
5114
+ type: "positional"
5115
+ },
5116
+ dir: {
5117
+ default: ".",
5118
+ description: "Published directory",
5119
+ type: "positional"
5120
+ },
5121
+ json: { description: "Machine-readable output", type: "boolean" },
5122
+ limit: { description: "Max records to show", type: "string" }
5123
+ },
5124
+ meta: {
5125
+ description: "List records in a collection",
5126
+ name: "ls"
5127
+ },
5128
+ async run({ args }) {
5129
+ try {
5130
+ await dataLs({
5131
+ collection: args.collection,
5132
+ dir: args.dir,
5133
+ json: Boolean(args.json),
5134
+ limit: args.limit === void 0 ? void 0 : Number(args.limit)
5135
+ });
5136
+ } catch (err) {
5137
+ printCliError(err, Boolean(args.json));
5138
+ process.exit(1);
5139
+ }
5140
+ }
5141
+ });
5142
+ var dataExportCommand = defineCommand({
5143
+ args: {
5144
+ dir: {
5145
+ default: ".",
5146
+ description: "Published directory",
5147
+ type: "positional"
5148
+ },
5149
+ // Output is always JSON; accepted so --json works uniformly across
5150
+ // every command (SPEC-2 milestone 20 → CLI integration).
5151
+ json: {
5152
+ description: "Machine-readable output (always on)",
5153
+ type: "boolean"
5154
+ }
5155
+ },
5156
+ meta: {
5157
+ description: "Export every collection's records as JSON",
5158
+ name: "export"
5159
+ },
5160
+ async run({ args }) {
5161
+ try {
5162
+ await dataExport({ dir: args.dir });
5163
+ } catch (err) {
5164
+ printCliError(err, true);
5165
+ process.exit(1);
5166
+ }
5167
+ }
5168
+ });
5169
+ var dataClearCommand = defineCommand({
5170
+ args: {
5171
+ collection: {
5172
+ description: "Collection name",
5173
+ required: true,
5174
+ type: "positional"
5175
+ },
5176
+ dir: {
5177
+ default: ".",
5178
+ description: "Published directory",
5179
+ type: "positional"
5180
+ },
5181
+ json: { description: "Machine-readable output", type: "boolean" },
5182
+ yes: { description: "Confirm deleting every record", type: "boolean" }
5183
+ },
5184
+ meta: {
5185
+ description: "Delete every record in a collection",
5186
+ name: "clear"
5187
+ },
5188
+ async run({ args }) {
5189
+ try {
5190
+ await dataClear({
5191
+ collection: args.collection,
5192
+ dir: args.dir,
5193
+ json: Boolean(args.json),
5194
+ yes: Boolean(args.yes)
5195
+ });
5196
+ } catch (err) {
5197
+ printCliError(err, Boolean(args.json));
5198
+ process.exit(1);
5199
+ }
5200
+ }
5201
+ });
5202
+ var data = defineCommand({
5203
+ meta: {
5204
+ description: "Read or clear the artifact's shared records",
5205
+ name: "data"
5206
+ },
5207
+ subCommands: {
5208
+ clear: dataClearCommand,
5209
+ export: dataExportCommand,
5210
+ ls: dataLsCommand
5211
+ }
5212
+ });
4602
5213
  var main = defineCommand({
4603
5214
  meta: {
4604
5215
  description: "Publish AI-generated artifacts for review \u2014 https://artifact.cafe",
@@ -4621,6 +5232,7 @@ var main = defineCommand({
4621
5232
  comment: comment2,
4622
5233
  comments: comments2,
4623
5234
  dashboard,
5235
+ data,
4624
5236
  delete: del,
4625
5237
  edit,
4626
5238
  folders: folders2,