tands 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/tands.js CHANGED
@@ -2580,8 +2580,8 @@ var Doc = class {
2580
2580
  const lines = content.split("\n").filter((x) => x);
2581
2581
  const minIndent = Math.min(...lines.map((x) => x.length - x.trimStart().length));
2582
2582
  const dedented = lines.map((x) => x.slice(minIndent)).map((x) => " ".repeat(this.indent * 2) + x);
2583
- for (const line of dedented) {
2584
- this.content.push(line);
2583
+ for (const line2 of dedented) {
2584
+ this.content.push(line2);
2585
2585
  }
2586
2586
  }
2587
2587
  compile() {
@@ -19676,9 +19676,101 @@ function date4(params) {
19676
19676
  return _coercedDate(ZodDate, params);
19677
19677
  }
19678
19678
 
19679
+ // ../types/src/time.ts
19680
+ function isTimeZone(value) {
19681
+ if (!value || value.length > 64) return false;
19682
+ try {
19683
+ new Intl.DateTimeFormat("en", { timeZone: value });
19684
+ return true;
19685
+ } catch {
19686
+ return false;
19687
+ }
19688
+ }
19689
+ var TimeZoneSchema = external_exports.string().refine(isTimeZone, "Not a known time zone.");
19690
+ var DEFAULT_TIME_ZONE = "UTC";
19691
+
19692
+ // ../types/src/recurrence.ts
19693
+ var WeekdaySchema = external_exports.number().int().min(0).max(6);
19694
+ var RecurrenceSchema = external_exports.discriminatedUnion("freq", [
19695
+ external_exports.object({ freq: external_exports.literal("daily"), interval: external_exports.number().int().min(1).max(365) }),
19696
+ external_exports.object({
19697
+ freq: external_exports.literal("weekly"),
19698
+ interval: external_exports.number().int().min(1).max(52),
19699
+ /** The days of the week it happens on (Monday to Friday: every weekday). */
19700
+ weekdays: external_exports.array(WeekdaySchema).min(1).max(7)
19701
+ }),
19702
+ external_exports.object({
19703
+ freq: external_exports.literal("monthly"),
19704
+ interval: external_exports.number().int().min(1).max(12),
19705
+ on: external_exports.discriminatedUnion("kind", [
19706
+ /** A day of the month; in shorter months, their last day. */
19707
+ external_exports.object({ kind: external_exports.literal("day"), day: external_exports.number().int().min(1).max(31) }),
19708
+ /** The first … fourth, or last (-1), such weekday of the month. */
19709
+ external_exports.object({
19710
+ kind: external_exports.literal("weekday"),
19711
+ week: external_exports.union([external_exports.literal(1), external_exports.literal(2), external_exports.literal(3), external_exports.literal(4), external_exports.literal(-1)]),
19712
+ weekday: WeekdaySchema
19713
+ })
19714
+ ])
19715
+ })
19716
+ ]);
19717
+ var IsoDateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "A date like 2026-10-16.");
19718
+ var ClockTimeSchema = external_exports.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "A time like 17:00.");
19719
+ var DAY_MS = 24 * 60 * 6e4;
19720
+
19721
+ // ../types/src/notifications.ts
19722
+ var NOTIFICATION_KINDS = ["mention", "dm", "approval", "thread"];
19723
+ var NotificationKindSchema = external_exports.enum(NOTIFICATION_KINDS);
19724
+ var QuietHoursSchema = external_exports.object({
19725
+ on: external_exports.boolean().default(false),
19726
+ from: ClockTimeSchema.default("22:00"),
19727
+ to: ClockTimeSchema.default("08:00"),
19728
+ days: external_exports.array(external_exports.number().int().min(0).max(6)).max(7).default([0, 1, 2, 3, 4, 5, 6])
19729
+ });
19730
+ var NotificationSettingsSchema = external_exports.object({
19731
+ /** A gentle chime (and the Inbox icon's pulse) when something new arrives. */
19732
+ sound: external_exports.boolean().default(true),
19733
+ /** Which kinds chime and pop up (threads you follow: quiet, unless you add them). */
19734
+ alert: external_exports.array(NotificationKindSchema).default(["mention", "dm", "approval"]),
19735
+ /** Do not disturb (decision 194): quiet hours, each week. */
19736
+ quietHours: QuietHoursSchema.default(QuietHoursSchema.parse({})),
19737
+ /** Paused until then (a quick "for an hour"); null: not paused. */
19738
+ pausedUntil: external_exports.iso.datetime().nullable().default(null),
19739
+ /** What still gets through while you're quiet (a DM, a request for your yes). */
19740
+ allowWhenQuiet: external_exports.array(NotificationKindSchema).default([]),
19741
+ /**
19742
+ * Away from the app (decision 195): what's emailed when it's still unread a
19743
+ * little later (grouped into one email), and what's sent to your linked chats.
19744
+ */
19745
+ email: external_exports.array(NotificationKindSchema).default(["mention", "dm", "approval"]),
19746
+ chat: external_exports.array(NotificationKindSchema).default(["mention", "dm", "approval"]),
19747
+ /** A short email when something has waited unread for hours (at most one a day). */
19748
+ reminder: external_exports.boolean().default(true)
19749
+ });
19750
+ var DEFAULT_NOTIFICATION_SETTINGS = NotificationSettingsSchema.parse(
19751
+ {}
19752
+ );
19753
+ var UpdateNotificationSettingsSchema = external_exports.object({
19754
+ sound: external_exports.boolean(),
19755
+ alert: external_exports.array(NotificationKindSchema).max(NOTIFICATION_KINDS.length),
19756
+ quietHours: QuietHoursSchema,
19757
+ pausedUntil: external_exports.iso.datetime().nullable(),
19758
+ allowWhenQuiet: external_exports.array(NotificationKindSchema).max(NOTIFICATION_KINDS.length),
19759
+ email: external_exports.array(NotificationKindSchema).max(NOTIFICATION_KINDS.length),
19760
+ chat: external_exports.array(NotificationKindSchema).max(NOTIFICATION_KINDS.length),
19761
+ reminder: external_exports.boolean()
19762
+ }).partial().strict();
19763
+ var NotificationSettingsResponseSchema = external_exports.object({
19764
+ settings: NotificationSettingsSchema
19765
+ });
19766
+ var SPACE_NOTIFY_LEVELS = ["all", "mentions", "muted"];
19767
+ var SpaceNotifySchema = external_exports.enum(SPACE_NOTIFY_LEVELS);
19768
+ var SetSpaceNotifyRequestSchema = external_exports.object({ level: SpaceNotifySchema });
19769
+
19679
19770
  // ../types/src/apps.ts
19680
19771
  var APP_MANIFEST_VERSION = 1;
19681
19772
  var MAX_APP_TABS = 10;
19773
+ var MAX_APP_HOME_PAGES = 3;
19682
19774
  var APP_TOKEN_SECONDS = 600;
19683
19775
  var APP_DATA_LIMIT_BYTES = 100 * 1024 * 1024;
19684
19776
  var APP_RECORD_MAX_BYTES = 32 * 1024;
@@ -19722,7 +19814,20 @@ var AppActionSchema = external_exports.object({
19722
19814
  /** It changes something (the default): agents ask first. `false`: it only reads. */
19723
19815
  changes: external_exports.boolean().default(true),
19724
19816
  /** What it's given, checked like a record's fields. */
19725
- input: external_exports.record(DataNameSchema, AppFieldSchema).refine((fields) => Object.keys(fields).length <= 20, "An action takes at most 20 inputs.").default({})
19817
+ input: external_exports.record(DataNameSchema, AppFieldSchema).refine((fields) => Object.keys(fields).length <= 20, "An action takes at most 20 inputs.").default({}),
19818
+ /**
19819
+ * Its view (decision 216): a page on the app's origin shown under the agent's
19820
+ * answer, told what the action was asked and answered (MCP Apps). Only for
19821
+ * actions that only read, for now.
19822
+ */
19823
+ view: external_exports.object({
19824
+ url: external_exports.string().min(1).max(500),
19825
+ /** What it's called above the page (default: the action's name). */
19826
+ title: external_exports.string().trim().min(1).max(60).optional()
19827
+ }).optional()
19828
+ }).refine((action) => !action.view || !action.changes, {
19829
+ message: "Only an action that only reads (changes: false) can have a view, for now.",
19830
+ path: ["view"]
19726
19831
  });
19727
19832
  var AppEventSchema = external_exports.object({
19728
19833
  /** What happened, for people choosing it in a trigger. */
@@ -19751,6 +19856,18 @@ var AppManifestSchema = external_exports.object({
19751
19856
  url: external_exports.string().min(1).max(500)
19752
19857
  })
19753
19858
  ).min(1, "An app needs at least one tab.").max(MAX_APP_TABS),
19859
+ /**
19860
+ * Home pages it offers (decision 177): an owner or admin may choose one as the
19861
+ * workspace's Home. Shown like a tab, without a space around it.
19862
+ */
19863
+ home: external_exports.array(
19864
+ external_exports.object({
19865
+ id: AppTabIdSchema,
19866
+ name: external_exports.string().trim().min(1).max(40),
19867
+ description: external_exports.string().trim().max(200).default(""),
19868
+ url: external_exports.string().min(1).max(500)
19869
+ })
19870
+ ).max(MAX_APP_HOME_PAGES).optional(),
19754
19871
  /** The data it keeps in the workspace (M9 slice 2). */
19755
19872
  data: external_exports.object({
19756
19873
  collections: external_exports.record(DataNameSchema, AppCollectionSchema).refine(
@@ -19761,12 +19878,21 @@ var AppManifestSchema = external_exports.object({
19761
19878
  /** What it does when agents ask (M9 slice 3). */
19762
19879
  actions: external_exports.record(DataNameSchema, AppActionSchema).refine((actions) => Object.keys(actions).length <= 20, "An app has at most 20 actions.").optional(),
19763
19880
  /** What happens in it, for triggers to listen for (M9 slice 5). */
19764
- events: external_exports.record(DataNameSchema, AppEventSchema).refine((events) => Object.keys(events).length <= 20, "An app has at most 20 events.").optional()
19881
+ events: external_exports.record(DataNameSchema, AppEventSchema).refine((events) => Object.keys(events).length <= 20, "An app has at most 20 events.").optional(),
19882
+ /**
19883
+ * Publishing it to the marketplace (decision 203): `verify` is the token of
19884
+ * the workspace that may publish it, shown on its Publish page. Only whoever
19885
+ * controls the manifest's address can put it there.
19886
+ */
19887
+ marketplace: external_exports.object({ verify: external_exports.string().trim().max(100) }).optional()
19765
19888
  });
19889
+ var ManifestHashSchema = external_exports.string().regex(/^[0-9a-f]{64}$/, "A manifest's hash.");
19766
19890
  var AppActionInfoSchema = external_exports.object({
19767
19891
  name: external_exports.string(),
19768
19892
  description: external_exports.string(),
19769
19893
  changes: external_exports.boolean(),
19894
+ /** It shows its own view under the agent's answer (decision 216). */
19895
+ view: external_exports.boolean().default(false),
19770
19896
  input: external_exports.array(
19771
19897
  external_exports.object({
19772
19898
  name: external_exports.string(),
@@ -19823,6 +19949,8 @@ var AppSchema = external_exports.object({
19823
19949
  origin: external_exports.string(),
19824
19950
  manifestUrl: external_exports.string(),
19825
19951
  tabs: external_exports.array(AppTabSchema),
19952
+ /** Home pages it offers (decision 177). */
19953
+ homePages: external_exports.array(AppTabSchema).default([]),
19826
19954
  /** The data it keeps (M9 slice 2). */
19827
19955
  collections: external_exports.array(AppCollectionInfoSchema),
19828
19956
  /** What it does when agents ask (M9 slice 3). */
@@ -19834,7 +19962,14 @@ var AppSchema = external_exports.object({
19834
19962
  dataLimitBytes: external_exports.number(),
19835
19963
  installedBy: external_exports.object({ userId: external_exports.string(), name: external_exports.string() }),
19836
19964
  installedAt: external_exports.string(),
19837
- updatedAt: external_exports.string()
19965
+ updatedAt: external_exports.string(),
19966
+ /** The marketplace listing it was installed from (decision 205; null: by its address). */
19967
+ fromListing: external_exports.object({ id: external_exports.string(), slug: external_exports.string() }).nullable().default(null),
19968
+ /**
19969
+ * Tabs & Spaces took that listing down (decision 205), and why: it still
19970
+ * works here, for the workspace's owners and admins to decide.
19971
+ */
19972
+ takenDown: external_exports.object({ reason: external_exports.string(), at: external_exports.string() }).nullable().default(null)
19838
19973
  });
19839
19974
  var AppListSchema = external_exports.object({
19840
19975
  apps: external_exports.array(AppSchema),
@@ -19843,7 +19978,9 @@ var AppListSchema = external_exports.object({
19843
19978
  });
19844
19979
  var AppResponseSchema = external_exports.object({ app: AppSchema });
19845
19980
  var InstallAppRequestSchema = external_exports.object({
19846
- manifestUrl: external_exports.url({ protocol: /^https?$/, error: "Enter the manifest's full address." })
19981
+ manifestUrl: external_exports.url({ protocol: /^https?$/, error: "Enter the manifest's full address." }),
19982
+ /** From a listing (decision 203): only the manifest it reviewed, by its hash. */
19983
+ manifestHash: ManifestHashSchema.optional()
19847
19984
  });
19848
19985
  var AppPreviewSchema = external_exports.object({
19849
19986
  app: AppSchema.omit({
@@ -19852,7 +19989,9 @@ var AppPreviewSchema = external_exports.object({
19852
19989
  updatedAt: true,
19853
19990
  iconUrl: true,
19854
19991
  dataBytes: true,
19855
- dataLimitBytes: true
19992
+ dataLimitBytes: true,
19993
+ fromListing: true,
19994
+ takenDown: true
19856
19995
  }),
19857
19996
  /** Its picture, inline (it isn't kept until installed). */
19858
19997
  icon: external_exports.string().nullable(),
@@ -19862,7 +20001,17 @@ var AppPreviewSchema = external_exports.object({
19862
20001
  var TabAppSchema = external_exports.object({ appId: external_exports.string(), tabId: external_exports.string() });
19863
20002
  var AppContextSchema = external_exports.object({
19864
20003
  user: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
19865
- workspace: external_exports.object({ id: external_exports.string(), slug: external_exports.string(), name: external_exports.string() }),
20004
+ /**
20005
+ * `readOnly` (decision 215): the workspace is read-only until its bill is
20006
+ * paid, so the app's writes are refused; its page and server can say so.
20007
+ * (False when a token from before it was told says nothing.)
20008
+ */
20009
+ workspace: external_exports.object({
20010
+ id: external_exports.string(),
20011
+ slug: external_exports.string(),
20012
+ name: external_exports.string(),
20013
+ readOnly: external_exports.boolean().default(false)
20014
+ }),
19866
20015
  /**
19867
20016
  * `name` is the one the reader sees: General and My Space in their language,
19868
20017
  * a DM by its other people. Not imported from ./space, which imports this file.
@@ -19876,11 +20025,30 @@ var AppContextSchema = external_exports.object({
19876
20025
  app: external_exports.object({ id: external_exports.string() }),
19877
20026
  locale: external_exports.string()
19878
20027
  });
20028
+ var AppHomeContextSchema = AppContextSchema.extend({
20029
+ user: external_exports.object({ id: external_exports.string(), name: external_exports.string(), guest: external_exports.boolean() }),
20030
+ space: external_exports.null(),
20031
+ tab: external_exports.null(),
20032
+ /** Which of the app's home pages. */
20033
+ home: external_exports.object({ id: external_exports.string() })
20034
+ });
20035
+ var AppEventKeySchema = external_exports.object({ key: external_exports.string(), eventsUrl: external_exports.string() });
20036
+ var AppEventKeyStatusSchema = external_exports.object({
20037
+ on: external_exports.boolean(),
20038
+ /** Its app declares events (else there's nothing to report). */
20039
+ available: external_exports.boolean(),
20040
+ /** Who allowed it: events report for them. */
20041
+ by: external_exports.object({ id: external_exports.string(), name: external_exports.string() }).nullable(),
20042
+ createdAt: external_exports.string().nullable(),
20043
+ lastUsedAt: external_exports.string().nullable(),
20044
+ /** You may turn it on or off (you manage this space's tabs). */
20045
+ canManage: external_exports.boolean()
20046
+ });
19879
20047
  var AppTokenResponseSchema = external_exports.object({
19880
20048
  /** A JWT (EdDSA), verified with the deployment's keys at /api/apps/keys. */
19881
20049
  token: external_exports.string(),
19882
20050
  expiresAt: external_exports.string(),
19883
- context: AppContextSchema,
20051
+ context: external_exports.union([AppContextSchema, AppHomeContextSchema]),
19884
20052
  /** The frame's address and origin (messages go to and come from it only). */
19885
20053
  url: external_exports.string(),
19886
20054
  origin: external_exports.string()
@@ -20024,6 +20192,7 @@ var TabSettingsSchema = external_exports.object({
20024
20192
  });
20025
20193
  var AgentManagementSchema = external_exports.enum(["admins", "members"]);
20026
20194
  var TaskAssignmentSchema = external_exports.enum(["members", "admins"]);
20195
+ var GroupMentionsSchema = external_exports.enum(["members", "admins"]);
20027
20196
  var SpaceSchema = external_exports.object({
20028
20197
  id: SpaceIdSchema,
20029
20198
  kind: SpaceKindSchema,
@@ -20043,6 +20212,8 @@ var SpaceSchema = external_exports.object({
20043
20212
  unpinnedByYou: external_exports.boolean(),
20044
20213
  /** In your Starred section. */
20045
20214
  starred: external_exports.boolean(),
20215
+ /** How much it notifies you (decision 199): your own choice. */
20216
+ notify: SpaceNotifySchema.default("all"),
20046
20217
  /** DMs only: the participants (you included), so each reader can name it (§9.1). */
20047
20218
  people: external_exports.array(external_exports.object({ userId: external_exports.string(), name: external_exports.string() })),
20048
20219
  /** Your tab row: pinned first, then the rest in your (or the space's) order. */
@@ -20076,8 +20247,15 @@ var SpaceSchema = external_exports.object({
20076
20247
  canManage: external_exports.boolean(),
20077
20248
  /** Set once archived: hidden from sidebars, restorable or deletable by managers. */
20078
20249
  archivedAt: external_exports.iso.datetime().nullable(),
20079
- createdAt: external_exports.iso.datetime()
20080
- });
20250
+ createdAt: external_exports.iso.datetime(),
20251
+ /** Who may notify everyone here with `@space` and `@here` (decision 197). */
20252
+ groupMentions: GroupMentionsSchema.default("members"),
20253
+ /** You may: a member of a normal space as its rule says, General's for workspace admins. */
20254
+ canMentionEveryone: external_exports.boolean().default(false),
20255
+ /** Its icon (normal spaces and General), a picture its managers chose; versioned, so cacheable. */
20256
+ iconUrl: external_exports.string().nullable().default(null)
20257
+ });
20258
+ var MAX_SPACE_ICON_BYTES = 1024 * 1024;
20081
20259
  var SpaceListSchema = external_exports.object({
20082
20260
  spaces: external_exports.array(SpaceSchema),
20083
20261
  canCreate: external_exports.boolean()
@@ -20092,7 +20270,8 @@ var CreateSpaceRequestSchema = external_exports.object({
20092
20270
  var UpdateSpaceRequestSchema = external_exports.object({
20093
20271
  name: SpaceNameSchema,
20094
20272
  description: SpaceDescriptionSchema,
20095
- visibility: SpaceVisibilitySchema
20273
+ visibility: SpaceVisibilitySchema,
20274
+ groupMentions: GroupMentionsSchema
20096
20275
  }).partial().refine((patch) => Object.keys(patch).length > 0, "Nothing to change.");
20097
20276
  var ArchivedSpacesResponseSchema = external_exports.object({ spaces: external_exports.array(SpaceSchema) });
20098
20277
  var SpaceMemberSchema = external_exports.object({
@@ -20164,48 +20343,6 @@ var UpdateAgentSettingsRequestSchema = external_exports.object({
20164
20343
  respondingAgentId: external_exports.string().nullable()
20165
20344
  }).partial().refine((patch) => Object.keys(patch).length > 0, "Nothing to change.");
20166
20345
 
20167
- // ../types/src/time.ts
20168
- function isTimeZone(value) {
20169
- if (!value || value.length > 64) return false;
20170
- try {
20171
- new Intl.DateTimeFormat("en", { timeZone: value });
20172
- return true;
20173
- } catch {
20174
- return false;
20175
- }
20176
- }
20177
- var TimeZoneSchema = external_exports.string().refine(isTimeZone, "Not a known time zone.");
20178
- var DEFAULT_TIME_ZONE = "UTC";
20179
-
20180
- // ../types/src/recurrence.ts
20181
- var WeekdaySchema = external_exports.number().int().min(0).max(6);
20182
- var RecurrenceSchema = external_exports.discriminatedUnion("freq", [
20183
- external_exports.object({ freq: external_exports.literal("daily"), interval: external_exports.number().int().min(1).max(365) }),
20184
- external_exports.object({
20185
- freq: external_exports.literal("weekly"),
20186
- interval: external_exports.number().int().min(1).max(52),
20187
- /** The days of the week it happens on (Monday to Friday: every weekday). */
20188
- weekdays: external_exports.array(WeekdaySchema).min(1).max(7)
20189
- }),
20190
- external_exports.object({
20191
- freq: external_exports.literal("monthly"),
20192
- interval: external_exports.number().int().min(1).max(12),
20193
- on: external_exports.discriminatedUnion("kind", [
20194
- /** A day of the month; in shorter months, their last day. */
20195
- external_exports.object({ kind: external_exports.literal("day"), day: external_exports.number().int().min(1).max(31) }),
20196
- /** The first … fourth, or last (-1), such weekday of the month. */
20197
- external_exports.object({
20198
- kind: external_exports.literal("weekday"),
20199
- week: external_exports.union([external_exports.literal(1), external_exports.literal(2), external_exports.literal(3), external_exports.literal(4), external_exports.literal(-1)]),
20200
- weekday: WeekdaySchema
20201
- })
20202
- ])
20203
- })
20204
- ]);
20205
- var IsoDateSchema = external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "A date like 2026-10-16.");
20206
- var ClockTimeSchema = external_exports.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/, "A time like 17:00.");
20207
- var DAY_MS = 24 * 60 * 6e4;
20208
-
20209
20346
  // ../types/src/workflows.ts
20210
20347
  var MAX_WORKFLOW_STEPS = 30;
20211
20348
  var WorkflowStepIdSchema = external_exports.string().regex(/^[a-z0-9]{1,16}$/, "A step id.");
@@ -20394,6 +20531,7 @@ var FinishPersonStepRequestSchema = external_exports.object({ note: external_exp
20394
20531
  // ../types/src/triggers.ts
20395
20532
  var TaskStatusSchema = external_exports.enum(["todo", "in_progress", "waiting", "done", "cancelled"]);
20396
20533
  var MAX_TASK_ASSIGNEES = 10;
20534
+ var INBOX_TRIGGER_KINDS = ["mention", "dm", "thread"];
20397
20535
  var TriggerEventSchema = external_exports.discriminatedUnion("kind", [
20398
20536
  /** Someone posts in a conversation of the space (optionally with words in it, or by someone). */
20399
20537
  external_exports.object({
@@ -20428,6 +20566,14 @@ var TriggerEventSchema = external_exports.discriminatedUnion("kind", [
20428
20566
  event: external_exports.string().min(1).max(40),
20429
20567
  field: external_exports.string().max(40).nullable().default(null),
20430
20568
  value: external_exports.string().trim().max(200).nullable().default(null)
20569
+ }),
20570
+ /**
20571
+ * Something new in your Inbox (decision 198): a personal trigger, only in
20572
+ * your own My Space; what people (not agents) send you, from elsewhere.
20573
+ */
20574
+ external_exports.object({
20575
+ kind: external_exports.literal("inbox"),
20576
+ kinds: external_exports.array(external_exports.enum(INBOX_TRIGGER_KINDS)).min(1, "Choose what starts it.").max(INBOX_TRIGGER_KINDS.length).default(["mention", "dm"])
20431
20577
  })
20432
20578
  ]);
20433
20579
  var TriggerTaskSchema = external_exports.object({
@@ -20512,6 +20658,8 @@ var TaskRunStatusSchema = external_exports.enum([
20512
20658
  var TaskRunErrorSchema = external_exports.enum([
20513
20659
  "NOT_CONNECTED",
20514
20660
  "NOT_READY",
20661
+ // Every Gateway the deployment runs was busy, each time it tried (decision 121).
20662
+ "BUSY",
20515
20663
  "PROVIDER_REFUSED",
20516
20664
  "RUN_FAILED",
20517
20665
  "BUDGET_TURNS",
@@ -20654,7 +20802,13 @@ var TaskSeriesSchema = external_exports.object({
20654
20802
  lastAt: external_exports.string().nullable(),
20655
20803
  occurrences: external_exports.number().int(),
20656
20804
  createdBy: external_exports.object({ userId: external_exports.string(), name: external_exports.string() }),
20657
- createdAt: external_exports.string()
20805
+ createdAt: external_exports.string(),
20806
+ /** Made ready-made (decision 198): the unread report. */
20807
+ preset: external_exports.enum(["unread_report"]).nullable().default(null)
20808
+ });
20809
+ var UnreadReportResponseSchema = external_exports.object({ report: TaskSeriesSchema.nullable() });
20810
+ var CreateUnreadReportRequestSchema = external_exports.object({
20811
+ time: ClockTimeSchema.default("08:00")
20658
20812
  });
20659
20813
  var TaskSeriesResponseSchema = external_exports.object({ series: TaskSeriesSchema });
20660
20814
  var CreateTaskSeriesRequestSchema = external_exports.object({
@@ -20767,6 +20921,126 @@ var EmailDraftSchema = external_exports.object({
20767
20921
  error: external_exports.string().nullable()
20768
20922
  });
20769
20923
 
20924
+ // ../types/src/cards.ts
20925
+ var MAX_MESSAGE_CARDS = 8;
20926
+ var line = (max) => external_exports.string().trim().min(1).max(max);
20927
+ var optionalLine = (max) => external_exports.string().trim().max(max).optional();
20928
+ var link = external_exports.url({ protocol: /^https?$/, error: "An http(s) address." }).max(2e3);
20929
+ var when = external_exports.union([
20930
+ external_exports.string().regex(/^\d{4}-\d{2}-\d{2}$/, "A date like 2030-01-08."),
20931
+ external_exports.iso.datetime({ offset: true })
20932
+ ]);
20933
+ var tone = external_exports.enum(["good", "warning", "bad", "neutral"]);
20934
+ var title = optionalLine(120);
20935
+ var EmailsCardSchema = external_exports.object({
20936
+ type: external_exports.literal("emails"),
20937
+ title,
20938
+ items: external_exports.array(
20939
+ external_exports.object({
20940
+ from: line(120),
20941
+ subject: external_exports.string().trim().max(200),
20942
+ snippet: optionalLine(300),
20943
+ at: when.optional(),
20944
+ url: link.optional(),
20945
+ unread: external_exports.boolean().optional(),
20946
+ important: external_exports.boolean().optional(),
20947
+ /** Why it matters, or what to do. */
20948
+ note: optionalLine(240)
20949
+ })
20950
+ ).min(1).max(20)
20951
+ });
20952
+ var EventsCardSchema = external_exports.object({
20953
+ type: external_exports.literal("events"),
20954
+ title,
20955
+ items: external_exports.array(
20956
+ external_exports.object({
20957
+ title: line(160),
20958
+ start: when,
20959
+ end: when.optional(),
20960
+ where: optionalLine(160),
20961
+ people: external_exports.array(line(80)).max(20).optional(),
20962
+ url: link.optional(),
20963
+ /** A video call's link. */
20964
+ join: link.optional(),
20965
+ /** The asker's answer to it. */
20966
+ status: external_exports.enum(["accepted", "tentative", "declined", "needs_answer"]).optional(),
20967
+ note: optionalLine(240)
20968
+ })
20969
+ ).min(1).max(20)
20970
+ });
20971
+ var MetricsCardSchema = external_exports.object({
20972
+ type: external_exports.literal("metrics"),
20973
+ title,
20974
+ items: external_exports.array(
20975
+ external_exports.object({
20976
+ label: line(60),
20977
+ value: line(40),
20978
+ detail: optionalLine(80),
20979
+ tone: tone.optional()
20980
+ })
20981
+ ).min(1).max(6)
20982
+ });
20983
+ var TableCardSchema = external_exports.object({
20984
+ type: external_exports.literal("table"),
20985
+ title,
20986
+ columns: external_exports.array(line(60)).min(1).max(8),
20987
+ rows: external_exports.array(external_exports.array(external_exports.union([external_exports.string().max(200), external_exports.number(), external_exports.null()])).max(8)).min(1).max(50)
20988
+ }).refine((card) => card.rows.every((row) => row.length === card.columns.length), {
20989
+ error: "Every row needs one cell per column.",
20990
+ path: ["rows"]
20991
+ });
20992
+ var ListCardSchema = external_exports.object({
20993
+ type: external_exports.literal("list"),
20994
+ title,
20995
+ items: external_exports.array(
20996
+ external_exports.object({
20997
+ title: line(160),
20998
+ detail: optionalLine(240),
20999
+ /** On the side: a date, an amount, a status. */
21000
+ meta: optionalLine(60),
21001
+ url: link.optional(),
21002
+ tone: tone.optional()
21003
+ })
21004
+ ).min(1).max(30)
21005
+ });
21006
+ var LinksCardSchema = external_exports.object({
21007
+ type: external_exports.literal("links"),
21008
+ title,
21009
+ items: external_exports.array(external_exports.object({ label: line(120), url: link, detail: optionalLine(160) })).min(1).max(12)
21010
+ });
21011
+ var NoteCardSchema = external_exports.object({
21012
+ type: external_exports.literal("note"),
21013
+ tone: external_exports.enum(["info", "success", "warning", "danger"]).default("info"),
21014
+ title,
21015
+ text: line(600)
21016
+ });
21017
+ var BarsCardSchema = external_exports.object({
21018
+ type: external_exports.literal("bars"),
21019
+ title,
21020
+ /** After each value: "h", "%", "€". */
21021
+ unit: optionalLine(12),
21022
+ items: external_exports.array(external_exports.object({ label: line(60), value: external_exports.number().finite().nonnegative() })).min(1).max(12)
21023
+ });
21024
+ var ActionsCardSchema = external_exports.object({
21025
+ type: external_exports.literal("actions"),
21026
+ items: external_exports.array(external_exports.object({ label: line(40), prompt: line(500) })).min(1).max(4)
21027
+ });
21028
+ var MessageCardSchema = external_exports.discriminatedUnion("type", [
21029
+ EmailsCardSchema,
21030
+ EventsCardSchema,
21031
+ MetricsCardSchema,
21032
+ TableCardSchema,
21033
+ ListCardSchema,
21034
+ LinksCardSchema,
21035
+ NoteCardSchema,
21036
+ BarsCardSchema,
21037
+ ActionsCardSchema
21038
+ ]);
21039
+ var MESSAGE_CARD_TYPES = MessageCardSchema.options.map(
21040
+ (option) => option.shape.type.value
21041
+ );
21042
+ var MessageCardsSchema = external_exports.array(MessageCardSchema).max(MAX_MESSAGE_CARDS);
21043
+
20770
21044
  // ../types/src/approvals.ts
20771
21045
  var ApprovalStatusSchema = external_exports.enum(["pending", "approved", "declined", "expired"]);
20772
21046
  var ApprovalSchema = external_exports.object({
@@ -20785,6 +21059,8 @@ var ApprovalSchema = external_exports.object({
20785
21059
  }).nullable(),
20786
21060
  /** The message, as proposed (or as approved, when edited). */
20787
21061
  body: external_exports.string(),
21062
+ /** Cards posted with it (decision 211: a recurring task's results). */
21063
+ cards: MessageCardsSchema.optional(),
20788
21064
  edited: external_exports.boolean(),
20789
21065
  /** Why, in the agent's words (optional); a workflow's step: what it asks. */
20790
21066
  reason: external_exports.string().nullable(),
@@ -20810,7 +21086,9 @@ var ApprovalSchema = external_exports.object({
20810
21086
  description: external_exports.string(),
20811
21087
  input: external_exports.record(external_exports.string(), external_exports.unknown()),
20812
21088
  result: external_exports.string().nullable(),
20813
- error: external_exports.string().nullable()
21089
+ error: external_exports.string().nullable(),
21090
+ /** A connector's tool: whose account it uses (the workspace's, or the asker's own). */
21091
+ account: external_exports.enum(["workspace", "person"]).nullable().default(null)
20814
21092
  }).nullable().default(null),
20815
21093
  /** An email (M9 slice 4): from, to, subject; its text is `body` (editable before it's sent). */
20816
21094
  email: EmailDraftSchema.nullable().default(null),
@@ -20959,6 +21237,40 @@ var ChannelLoginSchema = external_exports.object({
20959
21237
  qr: external_exports.string().nullable()
20960
21238
  });
20961
21239
 
21240
+ // ../types/src/views.ts
21241
+ var MessageViewSchema = external_exports.object({
21242
+ id: external_exports.string().regex(/^[a-f0-9]{24}$/),
21243
+ /** Whose it is: a connector's (its page served by us, sandboxed), or an app's. */
21244
+ source: external_exports.object({ kind: external_exports.enum(["connector", "app"]), id: external_exports.string(), name: external_exports.string() }),
21245
+ /** The tool it shows (an app's: the action). */
21246
+ tool: external_exports.string(),
21247
+ /** An app's view: its page, on the app's origin (null: a connector's, served by us). */
21248
+ url: external_exports.string().nullable().default(null),
21249
+ title: external_exports.string().nullable(),
21250
+ /** Its server asks for a border (true), none (false), or leaves it to us (null). */
21251
+ border: external_exports.boolean().nullable()
21252
+ });
21253
+ var ViewToolResultSchema = external_exports.object({
21254
+ content: external_exports.array(external_exports.record(external_exports.string(), external_exports.unknown())),
21255
+ structuredContent: external_exports.record(external_exports.string(), external_exports.unknown()).optional(),
21256
+ isError: external_exports.boolean().optional()
21257
+ });
21258
+ var MessageViewDataSchema = external_exports.object({
21259
+ tool: external_exports.object({
21260
+ name: external_exports.string(),
21261
+ title: external_exports.string().nullable(),
21262
+ description: external_exports.string(),
21263
+ inputSchema: external_exports.record(external_exports.string(), external_exports.unknown())
21264
+ }),
21265
+ input: external_exports.record(external_exports.string(), external_exports.unknown()),
21266
+ result: ViewToolResultSchema
21267
+ });
21268
+ var ViewCallRequestSchema = external_exports.object({
21269
+ name: external_exports.string().min(1).max(200),
21270
+ arguments: external_exports.record(external_exports.string(), external_exports.unknown()).default({})
21271
+ });
21272
+ var ViewCallResponseSchema = external_exports.object({ result: ViewToolResultSchema });
21273
+
20962
21274
  // ../types/src/message.ts
20963
21275
  var MAX_MESSAGE_LENGTH = 1e4;
20964
21276
  var MessageIdSchema = external_exports.string().regex(/^[a-f0-9]{24}$/, "Invalid message id");
@@ -21049,7 +21361,11 @@ var AgentToolSchema = external_exports.enum([
21049
21361
  // An agent changing its own profile (title, face, instructions…) when asked, approved first.
21050
21362
  "update_my_profile",
21051
21363
  // Connectors (M12 slice 2): a remote MCP server's tools the workspace chose (reading ones).
21052
- "use_connector"
21364
+ "use_connector",
21365
+ // Notifications (decision 198): the asker's own Inbox, where only they read the answer.
21366
+ "read_inbox",
21367
+ // Cards (decision 211): mail, events, figures and rows shown under its answer.
21368
+ "show_cards"
21053
21369
  ]);
21054
21370
  var AgentStepSchema = external_exports.object({
21055
21371
  tool: AgentToolSchema,
@@ -21104,6 +21420,10 @@ var MessageSchema = external_exports.object({
21104
21420
  proposal: TaskProposalSchema.optional(),
21105
21421
  /** Something an agent wants to do, waiting for a person's yes (M8 slice 6). */
21106
21422
  approval: ApprovalSchema.optional(),
21423
+ /** What an agent's answer shows (decision 211): mail, events, figures, rows… */
21424
+ cards: MessageCardsSchema.optional(),
21425
+ /** Connectors' views in an agent's answer (MCP Apps, decision 213). */
21426
+ views: external_exports.array(MessageViewSchema).optional(),
21107
21427
  /** Sent from a messaging app (M13): Telegram… */
21108
21428
  via: MessageViaSchema.optional()
21109
21429
  });
@@ -21154,7 +21474,9 @@ var ReferenceSchema = external_exports.object({
21154
21474
  id: external_exports.string(),
21155
21475
  kind: external_exports.enum(["general", "personal", "normal", "dm"]),
21156
21476
  name: external_exports.string().nullable(),
21157
- people: external_exports.array(external_exports.object({ userId: external_exports.string(), name: external_exports.string() }))
21477
+ people: external_exports.array(external_exports.object({ userId: external_exports.string(), name: external_exports.string() })),
21478
+ /** Its icon, when it has one (decision 190). */
21479
+ iconUrl: external_exports.string().nullable().default(null)
21158
21480
  }).nullable(),
21159
21481
  tab: external_exports.object({
21160
21482
  id: external_exports.string(),
@@ -21453,6 +21775,10 @@ var AUDIT_ACTIONS = {
21453
21775
  "workspace.data_moved",
21454
21776
  "workspace.previous_data_deleted",
21455
21777
  "workspace.support_plan_changed",
21778
+ "workspace.billing_contact_changed",
21779
+ "workspace.card_saved",
21780
+ "workspace.card_removed",
21781
+ "workspace.statement_paid",
21456
21782
  "workspace.space_imported",
21457
21783
  "audit.exported",
21458
21784
  "audit.destination_added",
@@ -21463,12 +21789,15 @@ var AUDIT_ACTIONS = {
21463
21789
  "member.invite_link_created",
21464
21790
  "member.invitation_revoked",
21465
21791
  "member.joined",
21466
- "member.title_changed"
21792
+ "member.title_changed",
21793
+ "member.role_changed"
21467
21794
  ],
21468
21795
  spaces: [
21469
21796
  "space.created",
21470
21797
  "space.updated",
21471
21798
  "space.deleted",
21799
+ "space.icon_changed",
21800
+ "space.icon_removed",
21472
21801
  "space.archived",
21473
21802
  "space.unarchived",
21474
21803
  "space.converted",
@@ -21508,6 +21837,9 @@ var AUDIT_ACTIONS = {
21508
21837
  "agent.avatar_changed",
21509
21838
  "agent.host_access_changed",
21510
21839
  "agent.memory_changed",
21840
+ "agent.published",
21841
+ "agent.unpublished",
21842
+ "agent.added_from_marketplace",
21511
21843
  "approval.requested",
21512
21844
  "gateway.connected",
21513
21845
  "gateway.disconnected",
@@ -21536,6 +21868,14 @@ var AUDIT_ACTIONS = {
21536
21868
  "app.installed",
21537
21869
  "app.updated",
21538
21870
  "app.removed",
21871
+ "app.added_from_marketplace",
21872
+ "app.published",
21873
+ "app.unpublished",
21874
+ "app.publishing_key_created",
21875
+ "app.publishing_key_revoked",
21876
+ "app.taken_down",
21877
+ "app.event_key_allowed",
21878
+ "app.event_key_turned_off",
21539
21879
  "app.action_run",
21540
21880
  "connection.added",
21541
21881
  "connection.removed",
@@ -21573,6 +21913,10 @@ var AUDIT_ACTIONS = {
21573
21913
  "billing.stripe_connected",
21574
21914
  "billing.stripe_disconnected",
21575
21915
  "statement.changed",
21916
+ "statement.extended",
21917
+ "agreement.created",
21918
+ "agreement.updated",
21919
+ "agreement.deleted",
21576
21920
  "support.registered",
21577
21921
  "support.unregistered",
21578
21922
  "support.report_sent",
@@ -21632,6 +21976,7 @@ var AuditTargetSchema = external_exports.object({
21632
21976
  "workflow",
21633
21977
  "approval",
21634
21978
  "statement",
21979
+ "agreement",
21635
21980
  "ticket",
21636
21981
  "help_request",
21637
21982
  "skill",
@@ -21784,12 +22129,18 @@ var DeploymentSettingsFields = external_exports.object({
21784
22129
  * for you") keep it awake, never put to sleep (decision 151)? They pay its
21785
22130
  * hours where the deployment bills them.
21786
22131
  */
21787
- allowGatewayKeepAwake: external_exports.boolean()
22132
+ allowGatewayKeepAwake: external_exports.boolean(),
22133
+ /**
22134
+ * Workspaces browse and add from the marketplace (decision 161): Tabs &
22135
+ * Spaces' catalogue, reviewed by us. On unless its admins turn it off.
22136
+ */
22137
+ marketplace: external_exports.boolean()
21788
22138
  });
21789
22139
  var DeploymentSettingsSchema = DeploymentSettingsFields.extend({
21790
22140
  // Missing in older responses and fixtures: off.
21791
22141
  shareFeedback: external_exports.boolean().default(false),
21792
- allowGatewayKeepAwake: external_exports.boolean().default(false)
22142
+ allowGatewayKeepAwake: external_exports.boolean().default(false),
22143
+ marketplace: external_exports.boolean().default(true)
21793
22144
  });
21794
22145
  var DeploymentSettingsPatchSchema = DeploymentSettingsFields.partial().strict().refine((patch) => Object.keys(patch).length > 0, "Nothing to update");
21795
22146
  var DeploymentInfoSchema = external_exports.object({
@@ -22065,78 +22416,380 @@ var ReadinessSchema = external_exports.object({
22065
22416
  checks: external_exports.array(ReadinessCheckSchema)
22066
22417
  });
22067
22418
 
22068
- // ../types/src/accent.ts
22069
- var HexColorSchema = external_exports.string().regex(/^#[0-9a-fA-F]{6}$/, "Use a colour like #4f46e5").transform((hex3) => hex3.toLowerCase());
22070
- var WorkspaceAccentSchema = external_exports.object({ light: HexColorSchema, dark: HexColorSchema });
22071
-
22072
- // ../types/src/workspace.ts
22073
- var WorkspaceRoleSchema = external_exports.enum(["owner", "admin", "member", "guest"]);
22074
- var WorkspaceSlugSchema = external_exports.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/, "Use lowercase letters, numbers and hyphens");
22075
- var WorkspaceNameSchema = external_exports.string().trim().min(1).max(60);
22076
- var WorkspaceSchema = external_exports.object({
22077
- id: external_exports.string(),
22078
- slug: WorkspaceSlugSchema,
22079
- name: external_exports.string(),
22080
- role: WorkspaceRoleSchema,
22081
- createdAt: external_exports.iso.datetime(),
22082
- /** The workspace's logo (optional), served from its own storage; versioned, so cacheable. */
22083
- logoUrl: external_exports.string().nullable().default(null),
22084
- /** Its data is moving to new storage (M5 slice 4): read-only until it's done. */
22085
- moving: external_exports.boolean().default(false),
22086
- /** Its accent colour for each mode; null: the default ink. */
22087
- accent: WorkspaceAccentSchema.nullable().default(null),
22088
- /** Its default time zone (M8 slice 1): for schedules when a person's own isn't known. */
22089
- timeZone: TimeZoneSchema.default(DEFAULT_TIME_ZONE)
22090
- });
22091
- var MAX_WORKSPACE_LOGO_BYTES = 2 * 1024 * 1024;
22092
- var WorkspaceListSchema = external_exports.object({
22093
- workspaces: external_exports.array(WorkspaceSchema),
22094
- /** Whether the current user may create a(nother) workspace right now. */
22095
- canCreate: external_exports.boolean(),
22096
- /** Whether a new workspace may keep its data in its own MongoDB and bucket (M5). */
22097
- customInfrastructure: external_exports.boolean()
22098
- });
22099
- var MongoUriSchema = external_exports.string().trim().max(2e3).regex(/^mongodb(\+srv)?:\/\/[^\s]+$/, "Use a mongodb:// or mongodb+srv:// connection string");
22100
- var BucketSchema = external_exports.object({
22101
- endpoint: external_exports.string().trim().max(500).regex(/^https?:\/\/[^\s/@]+(\/[^\s]*)?$/, "Use an http(s) address, without credentials").optional(),
22102
- region: external_exports.string().trim().min(1).max(64).default("us-east-1"),
22103
- bucket: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/, "Use a valid bucket name"),
22104
- accessKeyId: external_exports.string().trim().min(1).max(256),
22105
- secretAccessKey: external_exports.string().min(1).max(512),
22106
- /** Path-style addresses (MinIO and most self-hosted stores need them). */
22107
- forcePathStyle: external_exports.boolean().default(false)
22108
- });
22109
- var STORAGE_PROBLEMS = [
22110
- "INVALID",
22111
- "UNREACHABLE",
22112
- "PRIVATE_ADDRESS",
22113
- "AUTH_FAILED",
22114
- "NO_BUCKET",
22115
- "NO_PERMISSION",
22116
- "NOT_EMPTY"
22419
+ // ../types/src/billing.ts
22420
+ var COMMON_CURRENCIES = [
22421
+ "USD",
22422
+ "EUR",
22423
+ "GBP",
22424
+ "NGN",
22425
+ "KES",
22426
+ "GHS",
22427
+ "ZAR",
22428
+ "CAD",
22429
+ "AUD",
22430
+ "CHF",
22431
+ "JPY",
22432
+ "INR",
22433
+ "BRL"
22117
22434
  ];
22118
- var StorageProblemSchema = external_exports.enum(STORAGE_PROBLEMS);
22119
- var StorageCheckSchema = external_exports.discriminatedUnion("ok", [
22120
- external_exports.object({
22121
- ok: external_exports.literal(true),
22122
- /** Where it is: "s3.eu-west-1.amazonaws.com" or the endpoint's host. */
22123
- host: external_exports.string(),
22124
- bucket: external_exports.string()
22125
- }),
22126
- external_exports.object({ ok: external_exports.literal(false), problem: StorageProblemSchema, detail: external_exports.string() })
22127
- ]);
22128
- var CreateWorkspaceRequestSchema = external_exports.object({
22129
- name: WorkspaceNameSchema,
22130
- /** Optional explicit address; derived from the name when omitted. */
22131
- slug: WorkspaceSlugSchema.optional(),
22132
- /** Keep the workspace's data in this MongoDB instead of the deployment's (M5 slice 1). */
22133
- database: external_exports.object({ uri: MongoUriSchema }).optional(),
22134
- /** Keep its files in this bucket instead of the deployment's (M5 slice 2). */
22135
- files: BucketSchema.optional(),
22136
- /** The creating browser's time zone, used when the account has none yet (M8 slice 1). */
22137
- timeZone: TimeZoneSchema.optional()
22435
+ var CurrencySchema = external_exports.string().regex(/^[A-Z]{3}$/, "Use a three-letter currency code, like USD").refine((code) => knownCurrencies().has(code), "Unknown currency");
22436
+ var known = null;
22437
+ function knownCurrencies() {
22438
+ known ??= new Set(
22439
+ typeof Intl.supportedValuesOf === "function" ? Intl.supportedValuesOf("currency") : COMMON_CURRENCIES
22440
+ );
22441
+ return known;
22442
+ }
22443
+ var PriceSchema = external_exports.number().min(0, "Prices can't be negative").max(1e6).transform((value) => Math.round(value * 1e4) / 1e4);
22444
+ var priced = external_exports.object({ enabled: external_exports.boolean(), price: PriceSchema });
22445
+ var ModelPriceSchema = external_exports.object({
22446
+ /** As agents name it: `provider/model`. */
22447
+ model: external_exports.string().trim().min(3).max(200),
22448
+ input: PriceSchema,
22449
+ /** Input read from the prompt cache. */
22450
+ cachedInput: PriceSchema,
22451
+ /** Input written to the prompt cache. */
22452
+ cacheWrite: PriceSchema,
22453
+ output: PriceSchema
22138
22454
  });
22139
- var DatabaseCheckRequestSchema = external_exports.object({ uri: MongoUriSchema });
22455
+ var BillingSettingsSchema = external_exports.object({
22456
+ currency: CurrencySchema,
22457
+ /** Per person who used the workspace in the month. */
22458
+ seats: priced,
22459
+ /** Per GB-month in the deployment's bucket. */
22460
+ files: priced,
22461
+ /** Per GB-month in the deployment's MongoDB. */
22462
+ database: priced,
22463
+ /** Model cost (from `models`) plus a markup, for runs on the deployment's Gateway. */
22464
+ ai: external_exports.object({
22465
+ enabled: external_exports.boolean(),
22466
+ markupPercent: external_exports.number().min(0).max(1e3),
22467
+ models: external_exports.array(ModelPriceSchema).max(100).refine(
22468
+ (models) => new Set(models.map((m) => m.model)).size === models.length,
22469
+ "Each model once"
22470
+ )
22471
+ }),
22472
+ /** A flat amount per workspace per month (prorated from the day it was created). */
22473
+ deploymentFee: priced,
22474
+ /**
22475
+ * A support plan per workspace per month, opt-in (decision 74): workspaces
22476
+ * that turn it on pay it (prorated by day) and can ask the deployment for
22477
+ * help. Absent in settings saved before it existed: off.
22478
+ */
22479
+ support: priced.default({ enabled: false, price: 0 }),
22480
+ /**
22481
+ * The deployment's Gateways (decision 151), per hour: a workspace's own
22482
+ * ("Managed for you") while it's awake, or the deployment's shared one while
22483
+ * the workspace is connected to it. Absent in older settings: off.
22484
+ */
22485
+ gatewayHours: priced.default({ enabled: false, price: 0 }),
22486
+ /** A workspace's own Gateway's storage (its volume), per GB-month, asleep or awake. */
22487
+ gatewayStorage: priced.default({ enabled: false, price: 0 }),
22488
+ /**
22489
+ * What happens when a statement isn't paid (decision 209): reminders always;
22490
+ * with `readOnly`, the workspace becomes read-only `graceDays` after its
22491
+ * due date, until it's paid. Absent in older settings: on, 30 days.
22492
+ */
22493
+ overdue: external_exports.object({ readOnly: external_exports.boolean(), graceDays: external_exports.number().int().min(1).max(365) }).default({ readOnly: true, graceDays: 30 })
22494
+ });
22495
+ var BILLING_METERS = [
22496
+ "seats",
22497
+ "files",
22498
+ "database",
22499
+ "ai",
22500
+ "deploymentFee",
22501
+ "support",
22502
+ "gatewayHours",
22503
+ "gatewayStorage"
22504
+ ];
22505
+ var StripeConnectionSchema = external_exports.object({
22506
+ connected: external_exports.boolean(),
22507
+ /** Test keys create test invoices that nobody really pays. */
22508
+ mode: external_exports.enum(["test", "live"]).nullable(),
22509
+ /** The key's last four characters, to recognise it. */
22510
+ keyHint: external_exports.string().nullable(),
22511
+ connectedAt: external_exports.iso.datetime().nullable()
22512
+ });
22513
+ var ConnectStripeRequestSchema = external_exports.object({
22514
+ secretKey: external_exports.string().trim().regex(
22515
+ /^(sk|rk)_(test|live)_[A-Za-z0-9]{10,}$/,
22516
+ "Use a Stripe secret key (sk_\u2026) or restricted key (rk_\u2026)"
22517
+ )
22518
+ });
22519
+ var AdminBillingSchema = external_exports.object({
22520
+ settings: BillingSettingsSchema,
22521
+ /** Models agents have run on this deployment's Gateways, to price. */
22522
+ modelsInUse: external_exports.array(external_exports.string()),
22523
+ stripe: StripeConnectionSchema
22524
+ });
22525
+ var BillLineSchema = external_exports.object({
22526
+ meter: external_exports.enum([...BILLING_METERS, "agreement"]),
22527
+ /**
22528
+ * billed: charged; notBilled: this deployment doesn't bill it;
22529
+ * own: the workspace brings this resource itself; notChosen: an opt-in plan
22530
+ * (support) the workspace doesn't have; included: its agreement covers it.
22531
+ */
22532
+ status: external_exports.enum(["billed", "notBilled", "own", "notChosen", "included"]),
22533
+ /** People, GB-months, tokens, or the share of the month (deployment fee, support plan). */
22534
+ quantity: external_exports.number().nonnegative(),
22535
+ /** Price per unit; null for AI (priced per model). */
22536
+ unitPrice: external_exports.number().nullable(),
22537
+ amount: external_exports.number().nonnegative()
22538
+ });
22539
+ var WorkspaceBillSchema = external_exports.object({
22540
+ currency: external_exports.string(),
22541
+ lines: external_exports.array(BillLineSchema),
22542
+ /** AI amount per model (null: the model has no price set). */
22543
+ aiByModel: external_exports.array(external_exports.object({ model: external_exports.string(), amount: external_exports.number().nullable() })),
22544
+ /** Models used but not priced: their tokens aren't charged. */
22545
+ unpricedModels: external_exports.array(external_exports.string()),
22546
+ total: external_exports.number().nonnegative(),
22547
+ /** True for the current month: amounts grow until it ends. */
22548
+ toDate: external_exports.boolean()
22549
+ });
22550
+ var StatementStatusSchema = external_exports.enum(["nothingDue", "due", "paid", "void"]);
22551
+ var StatementSchema = external_exports.object({
22552
+ id: external_exports.string(),
22553
+ workspaceId: external_exports.string(),
22554
+ /**
22555
+ * usage: a month's metered bill, after it ends; agreement: a period of an
22556
+ * agreement's fixed price, at its start (decision 209).
22557
+ */
22558
+ kind: external_exports.enum(["usage", "agreement"]).default("usage"),
22559
+ /** usage: the month; agreement: the period's first month. */
22560
+ month: external_exports.string(),
22561
+ /** An agreement's period: its first and last months (null for usage). */
22562
+ period: external_exports.object({ from: external_exports.string(), to: external_exports.string() }).nullable().default(null),
22563
+ currency: external_exports.string(),
22564
+ lines: external_exports.array(BillLineSchema),
22565
+ aiByModel: external_exports.array(external_exports.object({ model: external_exports.string(), amount: external_exports.number().nullable() })),
22566
+ unpricedModels: external_exports.array(external_exports.string()),
22567
+ total: external_exports.number().nonnegative(),
22568
+ status: StatementStatusSchema,
22569
+ createdAt: external_exports.iso.datetime(),
22570
+ /** When it must be paid by (null: nothing due, or from before due dates were kept). */
22571
+ dueAt: external_exports.iso.datetime().nullable().default(null),
22572
+ paidAt: external_exports.iso.datetime().nullable(),
22573
+ payment: external_exports.object({
22574
+ /** stripe: an invoice on the deployment's Stripe; manual: settled outside the app. */
22575
+ provider: external_exports.enum(["stripe", "manual"]),
22576
+ invoiceNumber: external_exports.string().nullable(),
22577
+ /** Stripe's page where the invoice can be paid. */
22578
+ invoiceUrl: external_exports.url().nullable(),
22579
+ /** Why the invoice couldn't be created yet (retried every hour). */
22580
+ error: external_exports.string().nullable(),
22581
+ /** Why the saved card couldn't pay it (it can still be paid on Stripe's page). */
22582
+ chargeError: external_exports.string().nullable().default(null)
22583
+ })
22584
+ });
22585
+ var WorkspaceStatementsSchema = external_exports.object({ statements: external_exports.array(StatementSchema) });
22586
+ var AdminStatementsSchema = external_exports.object({
22587
+ month: external_exports.string(),
22588
+ /** Months with statements, newest first. */
22589
+ months: external_exports.array(external_exports.string()),
22590
+ statements: external_exports.array(
22591
+ StatementSchema.extend({
22592
+ workspace: external_exports.object({ id: external_exports.string(), name: external_exports.string(), slug: external_exports.string() })
22593
+ })
22594
+ )
22595
+ });
22596
+ var AgreementPeriodSchema = external_exports.enum(["month", "year"]);
22597
+ var AgreementSchema = external_exports.object({
22598
+ id: external_exports.string(),
22599
+ workspaceId: external_exports.string(),
22600
+ /** What it's called on invoices, like "Enterprise". */
22601
+ name: external_exports.string(),
22602
+ currency: external_exports.string(),
22603
+ /** The fixed price per period. */
22604
+ amount: external_exports.number().nonnegative(),
22605
+ period: AgreementPeriodSchema,
22606
+ startMonth: UsageMonthOf(),
22607
+ /** Its last month, included; null: until it's ended. */
22608
+ endMonth: UsageMonthOf().nullable(),
22609
+ included: external_exports.array(external_exports.enum(BILLING_METERS)),
22610
+ /** For the deployment's admins only (never shown to the workspace). */
22611
+ note: external_exports.string(),
22612
+ createdAt: external_exports.iso.datetime()
22613
+ });
22614
+ function UsageMonthOf() {
22615
+ return external_exports.string().regex(/^\d{4}-(0[1-9]|1[0-2])$/, "Use a month like 2026-11");
22616
+ }
22617
+ var agreementFields = {
22618
+ name: external_exports.string().trim().min(1, "Give it a name").max(80),
22619
+ amount: PriceSchema.refine((amount) => amount > 0, "Use an amount above 0"),
22620
+ period: AgreementPeriodSchema,
22621
+ startMonth: UsageMonthOf(),
22622
+ endMonth: UsageMonthOf().nullable(),
22623
+ included: external_exports.array(external_exports.enum(BILLING_METERS)).max(BILLING_METERS.length).transform((meters) => [...new Set(meters)]),
22624
+ note: external_exports.string().trim().max(2e3)
22625
+ };
22626
+ var endsAfterStart = (a) => !a.startMonth || !a.endMonth || a.endMonth >= a.startMonth;
22627
+ var CreateAgreementRequestSchema = external_exports.object({
22628
+ workspaceId: external_exports.string().min(1),
22629
+ ...agreementFields,
22630
+ note: agreementFields.note.default("")
22631
+ }).refine(endsAfterStart, { message: "It can't end before it starts", path: ["endMonth"] });
22632
+ var UpdateAgreementRequestSchema = external_exports.object(agreementFields).partial().refine((patch) => Object.keys(patch).length > 0, "Nothing to change.").refine(endsAfterStart, { message: "It can't end before it starts", path: ["endMonth"] });
22633
+ var AdminAgreementsSchema = external_exports.object({
22634
+ currency: external_exports.string(),
22635
+ /** The current month (UTC): agreements start then or later. */
22636
+ month: external_exports.string(),
22637
+ agreements: external_exports.array(
22638
+ AgreementSchema.extend({
22639
+ workspace: external_exports.object({ id: external_exports.string(), name: external_exports.string(), slug: external_exports.string() })
22640
+ })
22641
+ ),
22642
+ workspaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string(), slug: external_exports.string() }))
22643
+ });
22644
+ var BillingContactSchema = external_exports.object({
22645
+ /** The name on invoices: a company, or a person. */
22646
+ name: external_exports.string().trim().max(120),
22647
+ email: external_exports.union([external_exports.literal(""), external_exports.email()]),
22648
+ address: external_exports.object({
22649
+ line1: external_exports.string().trim().max(200),
22650
+ line2: external_exports.string().trim().max(200),
22651
+ city: external_exports.string().trim().max(100),
22652
+ postalCode: external_exports.string().trim().max(20),
22653
+ state: external_exports.string().trim().max(100),
22654
+ /** ISO 3166-1 alpha-2, or empty. */
22655
+ country: external_exports.union([
22656
+ external_exports.literal(""),
22657
+ external_exports.string().regex(/^[A-Z]{2}$/, "Use a two-letter country code")
22658
+ ])
22659
+ }),
22660
+ /** A VAT or tax number, printed on invoices. */
22661
+ taxId: external_exports.string().trim().max(50)
22662
+ });
22663
+ var SavedCardSchema = external_exports.object({
22664
+ brand: external_exports.string(),
22665
+ last4: external_exports.string(),
22666
+ expMonth: external_exports.number().int(),
22667
+ expYear: external_exports.number().int()
22668
+ });
22669
+ var BillingHoldSchema = external_exports.object({
22670
+ state: external_exports.enum(["overdue", "readOnly"]),
22671
+ /** The oldest unpaid statement's due date. */
22672
+ dueAt: external_exports.iso.datetime(),
22673
+ /** When it becomes read-only (null: the deployment doesn't do that). */
22674
+ readOnlyAt: external_exports.iso.datetime().nullable()
22675
+ });
22676
+ var WorkspaceBillingSchema = external_exports.object({
22677
+ /** null: the deployment doesn't bill workspaces. */
22678
+ plan: external_exports.discriminatedUnion("kind", [
22679
+ external_exports.object({
22680
+ kind: external_exports.literal("usage"),
22681
+ currency: external_exports.string(),
22682
+ /** The meters billed and their prices (null: priced per model, for AI). */
22683
+ meters: external_exports.array(
22684
+ external_exports.object({ meter: external_exports.enum(BILLING_METERS), unitPrice: external_exports.number().nullable() })
22685
+ )
22686
+ }),
22687
+ external_exports.object({
22688
+ kind: external_exports.literal("agreement"),
22689
+ currency: external_exports.string(),
22690
+ agreement: AgreementSchema.omit({ note: true }),
22691
+ /** The meters it doesn't include, billed as usual. */
22692
+ meters: external_exports.array(
22693
+ external_exports.object({ meter: external_exports.enum(BILLING_METERS), unitPrice: external_exports.number().nullable() })
22694
+ )
22695
+ })
22696
+ ]).nullable(),
22697
+ /** An agreement that starts later, if one is set. */
22698
+ upcoming: AgreementSchema.omit({ note: true }).nullable(),
22699
+ statements: external_exports.array(StatementSchema),
22700
+ contact: BillingContactSchema,
22701
+ /** Whether invoices can be paid online (the deployment has Stripe). */
22702
+ online: external_exports.boolean(),
22703
+ card: SavedCardSchema.nullable(),
22704
+ hold: BillingHoldSchema.nullable(),
22705
+ /** Owners change the contact and the card; admins see. */
22706
+ canManage: external_exports.boolean()
22707
+ });
22708
+ var CardSetupSchema = external_exports.object({ url: external_exports.url() });
22709
+
22710
+ // ../types/src/accent.ts
22711
+ var HexColorSchema = external_exports.string().regex(/^#[0-9a-fA-F]{6}$/, "Use a colour like #4f46e5").transform((hex3) => hex3.toLowerCase());
22712
+ var WorkspaceAccentSchema = external_exports.object({ light: HexColorSchema, dark: HexColorSchema });
22713
+
22714
+ // ../types/src/workspace.ts
22715
+ var WorkspaceRoleSchema = external_exports.enum(["owner", "admin", "member", "guest"]);
22716
+ var WorkspaceSlugSchema = external_exports.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/, "Use lowercase letters, numbers and hyphens");
22717
+ var WorkspaceNameSchema = external_exports.string().trim().min(1).max(60);
22718
+ var WorkspaceHomePageSchema = external_exports.object({
22719
+ appId: external_exports.string().min(1).max(64),
22720
+ pageId: external_exports.string().min(1).max(40)
22721
+ });
22722
+ var WorkspaceSchema = external_exports.object({
22723
+ id: external_exports.string(),
22724
+ slug: WorkspaceSlugSchema,
22725
+ name: external_exports.string(),
22726
+ role: WorkspaceRoleSchema,
22727
+ createdAt: external_exports.iso.datetime(),
22728
+ /** The workspace's logo (optional), served from its own storage; versioned, so cacheable. */
22729
+ logoUrl: external_exports.string().nullable().default(null),
22730
+ /** Its data is moving to new storage (M5 slice 4): read-only until it's done. */
22731
+ moving: external_exports.boolean().default(false),
22732
+ /**
22733
+ * Its bills are late (decision 209): owners and admins see it as overdue;
22734
+ * everyone sees it once it's read-only until paid. Null: nothing late.
22735
+ */
22736
+ billingHold: BillingHoldSchema.nullable().default(null),
22737
+ /** Its accent colour for each mode; null: the default ink. */
22738
+ accent: WorkspaceAccentSchema.nullable().default(null),
22739
+ /** Its default time zone (M8 slice 1): for schedules when a person's own isn't known. */
22740
+ timeZone: TimeZoneSchema.default(DEFAULT_TIME_ZONE),
22741
+ /** Its Home (decision 177): one of an installed app's home pages; null: ours. */
22742
+ homePage: WorkspaceHomePageSchema.nullable().default(null)
22743
+ });
22744
+ var MAX_WORKSPACE_LOGO_BYTES = 2 * 1024 * 1024;
22745
+ var WorkspaceListSchema = external_exports.object({
22746
+ workspaces: external_exports.array(WorkspaceSchema),
22747
+ /** Whether the current user may create a(nother) workspace right now. */
22748
+ canCreate: external_exports.boolean(),
22749
+ /** Whether a new workspace may keep its data in its own MongoDB and bucket (M5). */
22750
+ customInfrastructure: external_exports.boolean()
22751
+ });
22752
+ var MongoUriSchema = external_exports.string().trim().max(2e3).regex(/^mongodb(\+srv)?:\/\/[^\s]+$/, "Use a mongodb:// or mongodb+srv:// connection string");
22753
+ var BucketSchema = external_exports.object({
22754
+ endpoint: external_exports.string().trim().max(500).regex(/^https?:\/\/[^\s/@]+(\/[^\s]*)?$/, "Use an http(s) address, without credentials").optional(),
22755
+ region: external_exports.string().trim().min(1).max(64).default("us-east-1"),
22756
+ bucket: external_exports.string().trim().regex(/^[a-z0-9][a-z0-9.-]{1,61}[a-z0-9]$/, "Use a valid bucket name"),
22757
+ accessKeyId: external_exports.string().trim().min(1).max(256),
22758
+ secretAccessKey: external_exports.string().min(1).max(512),
22759
+ /** Path-style addresses (MinIO and most self-hosted stores need them). */
22760
+ forcePathStyle: external_exports.boolean().default(false)
22761
+ });
22762
+ var STORAGE_PROBLEMS = [
22763
+ "INVALID",
22764
+ "UNREACHABLE",
22765
+ "PRIVATE_ADDRESS",
22766
+ "AUTH_FAILED",
22767
+ "NO_BUCKET",
22768
+ "NO_PERMISSION",
22769
+ "NOT_EMPTY"
22770
+ ];
22771
+ var StorageProblemSchema = external_exports.enum(STORAGE_PROBLEMS);
22772
+ var StorageCheckSchema = external_exports.discriminatedUnion("ok", [
22773
+ external_exports.object({
22774
+ ok: external_exports.literal(true),
22775
+ /** Where it is: "s3.eu-west-1.amazonaws.com" or the endpoint's host. */
22776
+ host: external_exports.string(),
22777
+ bucket: external_exports.string()
22778
+ }),
22779
+ external_exports.object({ ok: external_exports.literal(false), problem: StorageProblemSchema, detail: external_exports.string() })
22780
+ ]);
22781
+ var CreateWorkspaceRequestSchema = external_exports.object({
22782
+ name: WorkspaceNameSchema,
22783
+ /** Optional explicit address; derived from the name when omitted. */
22784
+ slug: WorkspaceSlugSchema.optional(),
22785
+ /** Keep the workspace's data in this MongoDB instead of the deployment's (M5 slice 1). */
22786
+ database: external_exports.object({ uri: MongoUriSchema }).optional(),
22787
+ /** Keep its files in this bucket instead of the deployment's (M5 slice 2). */
22788
+ files: BucketSchema.optional(),
22789
+ /** The creating browser's time zone, used when the account has none yet (M8 slice 1). */
22790
+ timeZone: TimeZoneSchema.optional()
22791
+ });
22792
+ var DatabaseCheckRequestSchema = external_exports.object({ uri: MongoUriSchema });
22140
22793
  var DATABASE_PROBLEMS = [
22141
22794
  "INVALID",
22142
22795
  "UNREACHABLE",
@@ -22194,7 +22847,9 @@ var UpdateWorkspaceRequestSchema = external_exports.object({
22194
22847
  /** Owners and admins: the accent colour (null: back to the default). */
22195
22848
  accent: WorkspaceAccentSchema.nullable(),
22196
22849
  /** Owners and admins: the default time zone. */
22197
- timeZone: TimeZoneSchema
22850
+ timeZone: TimeZoneSchema,
22851
+ /** Owners and admins: Home, from an installed app (null: back to ours). */
22852
+ homePage: WorkspaceHomePageSchema.nullable()
22198
22853
  }).partial().strict().refine((patch) => Object.keys(patch).length > 0, "Nothing to update");
22199
22854
  var WorkspaceResponseSchema = external_exports.object({ workspace: WorkspaceSchema });
22200
22855
  var TRANSLITERATIONS = {
@@ -22267,10 +22922,13 @@ var WorkspaceMemberSchema = external_exports.object({
22267
22922
  title: external_exports.string().default(""),
22268
22923
  /** Their profile picture's address (null: initials), decision 145. */
22269
22924
  picture: external_exports.string().nullable().default(null),
22270
- joinedAt: external_exports.iso.datetime()
22925
+ joinedAt: external_exports.iso.datetime(),
22926
+ /** Their notifications are paused, or in quiet hours, until then (decision 194); null: not. */
22927
+ quietUntil: external_exports.iso.datetime().nullable().default(null)
22271
22928
  });
22272
22929
  var TitleSchema = external_exports.string().trim().max(60, "Keep the title under 60 characters.");
22273
22930
  var UpdateMemberRequestSchema = external_exports.object({ title: TitleSchema });
22931
+ var ChangeRoleRequestSchema = external_exports.object({ role: WorkspaceRoleSchema }).strict();
22274
22932
  var WorkspaceMemberListSchema = external_exports.object({ members: external_exports.array(WorkspaceMemberSchema) });
22275
22933
  var MAX_PROFILE_PICTURE_BYTES = 2 * 1024 * 1024;
22276
22934
 
@@ -22521,227 +23179,67 @@ var ExportManifestSchema = external_exports.object({
22521
23179
  scope: external_exports.enum(["shared", "space"]),
22522
23180
  /** The space, for a space export. */
22523
23181
  space: external_exports.object({
22524
- id: external_exports.string(),
22525
- kind: external_exports.enum(["general", "personal", "normal"]),
22526
- name: external_exports.string().nullable(),
22527
- description: external_exports.string(),
22528
- visibility: external_exports.enum(["public", "private"])
22529
- }).nullable().default(null),
22530
- exportedAt: external_exports.iso.datetime(),
22531
- exportedBy: external_exports.object({ name: external_exports.string(), email: external_exports.string() }),
22532
- workspace: external_exports.object({
22533
- id: external_exports.string(),
22534
- name: external_exports.string(),
22535
- slug: external_exports.string(),
22536
- createdAt: external_exports.iso.datetime(),
22537
- logo: external_exports.object({ key: external_exports.string(), contentType: external_exports.string() }).nullable()
22538
- }),
22539
- counts: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()),
22540
- files: external_exports.number().int().nonnegative()
22541
- });
22542
- var ExportPersonSchema = external_exports.object({
22543
- id: external_exports.string(),
22544
- name: external_exports.string(),
22545
- email: external_exports.string(),
22546
- role: external_exports.enum(["owner", "admin", "member", "guest"])
22547
- });
22548
- var ExportPeopleSchema = external_exports.array(ExportPersonSchema);
22549
- var ImportPreviewSchema = external_exports.object({
22550
- importId: external_exports.string(),
22551
- name: external_exports.string(),
22552
- /** What it holds: a whole workspace, or one space (and which kind). */
22553
- scope: external_exports.enum(["shared", "space"]).default("shared"),
22554
- spaceKind: external_exports.enum(["general", "personal", "normal"]).nullable().default(null),
22555
- exportedAt: external_exports.iso.datetime(),
22556
- exportedBy: external_exports.string(),
22557
- counts: external_exports.object({
22558
- spaces: external_exports.number().int(),
22559
- messages: external_exports.number().int(),
22560
- files: external_exports.number().int(),
22561
- agents: external_exports.number().int(),
22562
- people: external_exports.number().int()
22563
- })
22564
- });
22565
- var ImportRequestSchema = external_exports.object({
22566
- name: WorkspaceNameSchema,
22567
- slug: WorkspaceSlugSchema.optional(),
22568
- database: external_exports.object({ uri: MongoUriSchema }).optional(),
22569
- files: BucketSchema.optional()
22570
- });
22571
- var ImportResultSchema = external_exports.object({
22572
- workspace: WorkspaceSchema,
22573
- /** The other people from the export, to invite (their old messages keep their names). */
22574
- people: external_exports.array(external_exports.object({ name: external_exports.string(), email: external_exports.string() }))
22575
- });
22576
- var SpaceImportRequestSchema = external_exports.object({
22577
- /** For a new space: its name (the exported one when left out). */
22578
- name: external_exports.string().trim().min(1).max(80).optional()
22579
- });
22580
- var SpaceImportResultSchema = external_exports.object({
22581
- spaceId: external_exports.string(),
22582
- /** Messages and files brought in. */
22583
- messages: external_exports.number().int(),
22584
- files: external_exports.number().int()
22585
- });
22586
-
22587
- // ../types/src/billing.ts
22588
- var COMMON_CURRENCIES = [
22589
- "USD",
22590
- "EUR",
22591
- "GBP",
22592
- "NGN",
22593
- "KES",
22594
- "GHS",
22595
- "ZAR",
22596
- "CAD",
22597
- "AUD",
22598
- "CHF",
22599
- "JPY",
22600
- "INR",
22601
- "BRL"
22602
- ];
22603
- var CurrencySchema = external_exports.string().regex(/^[A-Z]{3}$/, "Use a three-letter currency code, like USD").refine((code) => knownCurrencies().has(code), "Unknown currency");
22604
- var known = null;
22605
- function knownCurrencies() {
22606
- known ??= new Set(
22607
- typeof Intl.supportedValuesOf === "function" ? Intl.supportedValuesOf("currency") : COMMON_CURRENCIES
22608
- );
22609
- return known;
22610
- }
22611
- var PriceSchema = external_exports.number().min(0, "Prices can't be negative").max(1e6).transform((value) => Math.round(value * 1e4) / 1e4);
22612
- var priced = external_exports.object({ enabled: external_exports.boolean(), price: PriceSchema });
22613
- var ModelPriceSchema = external_exports.object({
22614
- /** As agents name it: `provider/model`. */
22615
- model: external_exports.string().trim().min(3).max(200),
22616
- input: PriceSchema,
22617
- /** Input read from the prompt cache. */
22618
- cachedInput: PriceSchema,
22619
- /** Input written to the prompt cache. */
22620
- cacheWrite: PriceSchema,
22621
- output: PriceSchema
22622
- });
22623
- var BillingSettingsSchema = external_exports.object({
22624
- currency: CurrencySchema,
22625
- /** Per person who used the workspace in the month. */
22626
- seats: priced,
22627
- /** Per GB-month in the deployment's bucket. */
22628
- files: priced,
22629
- /** Per GB-month in the deployment's MongoDB. */
22630
- database: priced,
22631
- /** Model cost (from `models`) plus a markup, for runs on the deployment's Gateway. */
22632
- ai: external_exports.object({
22633
- enabled: external_exports.boolean(),
22634
- markupPercent: external_exports.number().min(0).max(1e3),
22635
- models: external_exports.array(ModelPriceSchema).max(100).refine(
22636
- (models) => new Set(models.map((m) => m.model)).size === models.length,
22637
- "Each model once"
22638
- )
22639
- }),
22640
- /** A flat amount per workspace per month (prorated from the day it was created). */
22641
- deploymentFee: priced,
22642
- /**
22643
- * A support plan per workspace per month, opt-in (decision 74): workspaces
22644
- * that turn it on pay it (prorated by day) and can ask the deployment for
22645
- * help. Absent in settings saved before it existed: off.
22646
- */
22647
- support: priced.default({ enabled: false, price: 0 }),
22648
- /**
22649
- * The deployment's Gateways (decision 151), per hour: a workspace's own
22650
- * ("Managed for you") while it's awake, or the deployment's shared one while
22651
- * the workspace is connected to it. Absent in older settings: off.
22652
- */
22653
- gatewayHours: priced.default({ enabled: false, price: 0 }),
22654
- /** A workspace's own Gateway's storage (its volume), per GB-month, asleep or awake. */
22655
- gatewayStorage: priced.default({ enabled: false, price: 0 })
22656
- });
22657
- var BILLING_METERS = [
22658
- "seats",
22659
- "files",
22660
- "database",
22661
- "ai",
22662
- "deploymentFee",
22663
- "support",
22664
- "gatewayHours",
22665
- "gatewayStorage"
22666
- ];
22667
- var StripeConnectionSchema = external_exports.object({
22668
- connected: external_exports.boolean(),
22669
- /** Test keys create test invoices that nobody really pays. */
22670
- mode: external_exports.enum(["test", "live"]).nullable(),
22671
- /** The key's last four characters, to recognise it. */
22672
- keyHint: external_exports.string().nullable(),
22673
- connectedAt: external_exports.iso.datetime().nullable()
22674
- });
22675
- var ConnectStripeRequestSchema = external_exports.object({
22676
- secretKey: external_exports.string().trim().regex(
22677
- /^(sk|rk)_(test|live)_[A-Za-z0-9]{10,}$/,
22678
- "Use a Stripe secret key (sk_\u2026) or restricted key (rk_\u2026)"
22679
- )
22680
- });
22681
- var AdminBillingSchema = external_exports.object({
22682
- settings: BillingSettingsSchema,
22683
- /** Models agents have run on this deployment's Gateways, to price. */
22684
- modelsInUse: external_exports.array(external_exports.string()),
22685
- stripe: StripeConnectionSchema
22686
- });
22687
- var BillLineSchema = external_exports.object({
22688
- meter: external_exports.enum(BILLING_METERS),
22689
- /**
22690
- * billed: charged; notBilled: this deployment doesn't bill it;
22691
- * own: the workspace brings this resource itself; notChosen: an opt-in plan
22692
- * (support) the workspace doesn't have.
22693
- */
22694
- status: external_exports.enum(["billed", "notBilled", "own", "notChosen"]),
22695
- /** People, GB-months, tokens, or the share of the month (deployment fee, support plan). */
22696
- quantity: external_exports.number().nonnegative(),
22697
- /** Price per unit; null for AI (priced per model). */
22698
- unitPrice: external_exports.number().nullable(),
22699
- amount: external_exports.number().nonnegative()
22700
- });
22701
- var WorkspaceBillSchema = external_exports.object({
22702
- currency: external_exports.string(),
22703
- lines: external_exports.array(BillLineSchema),
22704
- /** AI amount per model (null: the model has no price set). */
22705
- aiByModel: external_exports.array(external_exports.object({ model: external_exports.string(), amount: external_exports.number().nullable() })),
22706
- /** Models used but not priced: their tokens aren't charged. */
22707
- unpricedModels: external_exports.array(external_exports.string()),
22708
- total: external_exports.number().nonnegative(),
22709
- /** True for the current month: amounts grow until it ends. */
22710
- toDate: external_exports.boolean()
23182
+ id: external_exports.string(),
23183
+ kind: external_exports.enum(["general", "personal", "normal"]),
23184
+ name: external_exports.string().nullable(),
23185
+ description: external_exports.string(),
23186
+ visibility: external_exports.enum(["public", "private"])
23187
+ }).nullable().default(null),
23188
+ exportedAt: external_exports.iso.datetime(),
23189
+ exportedBy: external_exports.object({ name: external_exports.string(), email: external_exports.string() }),
23190
+ workspace: external_exports.object({
23191
+ id: external_exports.string(),
23192
+ name: external_exports.string(),
23193
+ slug: external_exports.string(),
23194
+ createdAt: external_exports.iso.datetime(),
23195
+ logo: external_exports.object({ key: external_exports.string(), contentType: external_exports.string() }).nullable()
23196
+ }),
23197
+ counts: external_exports.record(external_exports.string(), external_exports.number().int().nonnegative()),
23198
+ files: external_exports.number().int().nonnegative()
22711
23199
  });
22712
- var StatementStatusSchema = external_exports.enum(["nothingDue", "due", "paid", "void"]);
22713
- var StatementSchema = external_exports.object({
23200
+ var ExportPersonSchema = external_exports.object({
22714
23201
  id: external_exports.string(),
22715
- workspaceId: external_exports.string(),
22716
- month: external_exports.string(),
22717
- currency: external_exports.string(),
22718
- lines: external_exports.array(BillLineSchema),
22719
- aiByModel: external_exports.array(external_exports.object({ model: external_exports.string(), amount: external_exports.number().nullable() })),
22720
- unpricedModels: external_exports.array(external_exports.string()),
22721
- total: external_exports.number().nonnegative(),
22722
- status: StatementStatusSchema,
22723
- createdAt: external_exports.iso.datetime(),
22724
- paidAt: external_exports.iso.datetime().nullable(),
22725
- payment: external_exports.object({
22726
- /** stripe: an invoice on the deployment's Stripe; manual: settled outside the app. */
22727
- provider: external_exports.enum(["stripe", "manual"]),
22728
- invoiceNumber: external_exports.string().nullable(),
22729
- /** Stripe's page where the invoice can be paid. */
22730
- invoiceUrl: external_exports.url().nullable(),
22731
- /** Why the invoice couldn't be created yet (retried every hour). */
22732
- error: external_exports.string().nullable()
23202
+ name: external_exports.string(),
23203
+ email: external_exports.string(),
23204
+ role: external_exports.enum(["owner", "admin", "member", "guest"])
23205
+ });
23206
+ var ExportPeopleSchema = external_exports.array(ExportPersonSchema);
23207
+ var ImportPreviewSchema = external_exports.object({
23208
+ importId: external_exports.string(),
23209
+ name: external_exports.string(),
23210
+ /** What it holds: a whole workspace, or one space (and which kind). */
23211
+ scope: external_exports.enum(["shared", "space"]).default("shared"),
23212
+ spaceKind: external_exports.enum(["general", "personal", "normal"]).nullable().default(null),
23213
+ exportedAt: external_exports.iso.datetime(),
23214
+ exportedBy: external_exports.string(),
23215
+ counts: external_exports.object({
23216
+ spaces: external_exports.number().int(),
23217
+ messages: external_exports.number().int(),
23218
+ files: external_exports.number().int(),
23219
+ agents: external_exports.number().int(),
23220
+ people: external_exports.number().int()
22733
23221
  })
22734
23222
  });
22735
- var WorkspaceStatementsSchema = external_exports.object({ statements: external_exports.array(StatementSchema) });
22736
- var AdminStatementsSchema = external_exports.object({
22737
- month: external_exports.string(),
22738
- /** Months with statements, newest first. */
22739
- months: external_exports.array(external_exports.string()),
22740
- statements: external_exports.array(
22741
- StatementSchema.extend({
22742
- workspace: external_exports.object({ id: external_exports.string(), name: external_exports.string(), slug: external_exports.string() })
22743
- })
22744
- )
23223
+ var ImportRequestSchema = external_exports.object({
23224
+ name: WorkspaceNameSchema,
23225
+ slug: WorkspaceSlugSchema.optional(),
23226
+ database: external_exports.object({ uri: MongoUriSchema }).optional(),
23227
+ files: BucketSchema.optional()
23228
+ });
23229
+ var ImportResultSchema = external_exports.object({
23230
+ workspace: WorkspaceSchema,
23231
+ /** The other people from the export, to invite (their old messages keep their names). */
23232
+ people: external_exports.array(external_exports.object({ name: external_exports.string(), email: external_exports.string() }))
23233
+ });
23234
+ var SpaceImportRequestSchema = external_exports.object({
23235
+ /** For a new space: its name (the exported one when left out). */
23236
+ name: external_exports.string().trim().min(1).max(80).optional()
23237
+ });
23238
+ var SpaceImportResultSchema = external_exports.object({
23239
+ spaceId: external_exports.string(),
23240
+ /** Messages and files brought in. */
23241
+ messages: external_exports.number().int(),
23242
+ files: external_exports.number().int()
22745
23243
  });
22746
23244
 
22747
23245
  // ../types/src/usage.ts
@@ -23131,7 +23629,11 @@ var ConnectorToolSchema = external_exports.object({
23131
23629
  /** A tool its server doesn't describe, which an owner says only reads. */
23132
23630
  markedReadOnly: external_exports.boolean(),
23133
23631
  /** Agents may use it (only tools that read, or that an owner says only read). */
23134
- enabled: external_exports.boolean()
23632
+ enabled: external_exports.boolean(),
23633
+ /** It shows a view in conversations (MCP Apps, decision 213). */
23634
+ view: external_exports.boolean().default(false),
23635
+ /** Only its views call it, not agents. */
23636
+ viewsOnly: external_exports.boolean().default(false)
23135
23637
  });
23136
23638
  var McpConnectorSchema = external_exports.object({
23137
23639
  id: external_exports.string(),
@@ -23163,7 +23665,19 @@ var McpConnectorSchema = external_exports.object({
23163
23665
  addedAt: external_exports.string(),
23164
23666
  checkedAt: external_exports.string().nullable()
23165
23667
  });
23166
- var McpConnectorListSchema = external_exports.object({ connectors: external_exports.array(McpConnectorSchema) });
23668
+ var ConnectorPresetSchema = external_exports.object({
23669
+ id: external_exports.string(),
23670
+ name: external_exports.string(),
23671
+ url: external_exports.string(),
23672
+ auth: external_exports.enum(["oauth", "header"]),
23673
+ headerName: external_exports.string().nullable(),
23674
+ /** How its key is written ("Bearer github_pat_…"). */
23675
+ keyHint: external_exports.string().nullable()
23676
+ });
23677
+ var McpConnectorListSchema = external_exports.object({
23678
+ connectors: external_exports.array(McpConnectorSchema),
23679
+ presets: external_exports.array(ConnectorPresetSchema).default([])
23680
+ });
23167
23681
  var HeaderNameSchema = external_exports.string().trim().regex(/^[A-Za-z0-9-]{1,64}$/, "A header's name: letters, digits and dashes.").refine(
23168
23682
  (name) => !["host", "content-length", "content-type", "accept", "connection"].includes(
23169
23683
  name.toLowerCase()
@@ -23219,6 +23733,574 @@ var MyConnectorSchema = external_exports.object({
23219
23733
  var MyConnectorListSchema = external_exports.object({ connectors: external_exports.array(MyConnectorSchema) });
23220
23734
  var SetMyConnectorKeyRequestSchema = external_exports.object({ value: external_exports.string().trim().min(1, "Enter your key.").max(4e3) }).strict();
23221
23735
 
23736
+ // ../types/src/marketplace.ts
23737
+ var LISTING_KINDS = ["agent", "app"];
23738
+ var ListingKindSchema = external_exports.enum(LISTING_KINDS);
23739
+ var LISTING_CATEGORIES = [
23740
+ "productivity",
23741
+ "research",
23742
+ "writing",
23743
+ "support",
23744
+ "sales",
23745
+ "marketing",
23746
+ "engineering",
23747
+ "operations",
23748
+ "finance",
23749
+ "people",
23750
+ "education",
23751
+ "personal"
23752
+ ];
23753
+ var ListingCategorySchema = external_exports.enum(LISTING_CATEGORIES);
23754
+ var LISTED_AGENT = "@self";
23755
+ var LISTED_TABBY = "@tabby";
23756
+ var LISTED_ADDER = "@adder";
23757
+ var LISTED_TABS = {
23758
+ conversations: "@conversations",
23759
+ docs: "@docs",
23760
+ tasks: "@tasks"
23761
+ };
23762
+ var listedAppTab = (app) => `@app:${app.id}/${app.tab}`;
23763
+ var AGENT_PLACEHOLDERS = [LISTED_AGENT, LISTED_TABBY];
23764
+ var TAB_PLACEHOLDERS = Object.values(LISTED_TABS);
23765
+ var ASSIGNEE_PLACEHOLDERS = [LISTED_AGENT, LISTED_TABBY, LISTED_ADDER];
23766
+ var ListedAssigneesSchema = external_exports.array(external_exports.enum([LISTED_AGENT, LISTED_TABBY, LISTED_ADDER])).max(3).default([LISTED_AGENT]);
23767
+ var ListedSkillSchema = external_exports.discriminatedUnion("source", [
23768
+ external_exports.object({
23769
+ source: external_exports.literal("builtin"),
23770
+ name: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "A skill's name."),
23771
+ displayName: external_exports.string().trim().min(1).max(80)
23772
+ }),
23773
+ external_exports.object({
23774
+ source: external_exports.literal("clawhub"),
23775
+ ref: ClawHubRefSchema,
23776
+ displayName: external_exports.string().trim().min(1).max(80)
23777
+ })
23778
+ ]);
23779
+ var ListedConnectorSchema = external_exports.object({
23780
+ name: external_exports.string().trim().min(1).max(40),
23781
+ url: external_exports.url({ protocol: /^https$/, message: "A connector's https:// address." }).max(500),
23782
+ auth: external_exports.enum(["none", "header", "oauth"]),
23783
+ /** With a header: its name (its value is asked for when it's added). */
23784
+ headerName: external_exports.string().regex(/^[A-Za-z0-9-]{1,64}$/).nullable().default(null),
23785
+ identity: ConnectorIdentitySchema.default("workspace"),
23786
+ /** OAuth: what its sign-in asks for (null: what its server suggests, or our default for it). */
23787
+ scope: external_exports.string().trim().max(500).nullable().default(null),
23788
+ /** What it's for, for whoever adds it. */
23789
+ description: external_exports.string().trim().max(300).default(""),
23790
+ /** Its tools the agent may use, by name (the rest stay off). */
23791
+ tools: external_exports.array(external_exports.string().max(200)).max(500).default([])
23792
+ });
23793
+ var ListedAppSchema = external_exports.object({
23794
+ /** Its manifest's `id`. */
23795
+ id: external_exports.string().min(3).max(64),
23796
+ name: external_exports.string().trim().min(1).max(40),
23797
+ manifestUrl: external_exports.url({ protocol: /^https?$/, message: "The manifest's address." }).max(500),
23798
+ /** Which of its tabs goes in the space: `listedAppTab()` names it. */
23799
+ tab: external_exports.string().min(1).max(40),
23800
+ /** What it's for, for whoever adds it. */
23801
+ description: external_exports.string().trim().max(300).default("")
23802
+ });
23803
+ var ListedWorkflowSchema = external_exports.object({
23804
+ key: WorkflowStepIdSchema,
23805
+ name: external_exports.string().trim().min(1).max(120),
23806
+ description: external_exports.string().max(2e3).default(""),
23807
+ steps: external_exports.array(WorkflowStepSchema).min(1).max(30)
23808
+ });
23809
+ var ListedSeriesSchema = external_exports.object({
23810
+ title: external_exports.string().trim().min(1).max(200),
23811
+ description: external_exports.string().max(4e3).default(""),
23812
+ assignees: ListedAssigneesSchema,
23813
+ recurrence: RecurrenceSchema,
23814
+ time: ClockTimeSchema,
23815
+ /** Its results are also posted in a conversation (`@conversations`). */
23816
+ postTo: external_exports.literal(LISTED_TABS.conversations).nullable().default(null),
23817
+ approveResults: external_exports.boolean().default(false),
23818
+ workflowKey: WorkflowStepIdSchema.nullable().default(null)
23819
+ });
23820
+ var ListedTriggerSchema = external_exports.object({
23821
+ name: external_exports.string().trim().min(1).max(120),
23822
+ event: TriggerEventSchema,
23823
+ task: external_exports.object({
23824
+ title: external_exports.string().trim().min(1).max(200),
23825
+ description: external_exports.string().max(4e3).default(""),
23826
+ assignees: ListedAssigneesSchema,
23827
+ workflowKey: WorkflowStepIdSchema.nullable().default(null)
23828
+ })
23829
+ });
23830
+ var AgentListingContentSchema = external_exports.object({
23831
+ agent: external_exports.object({
23832
+ name: external_exports.string().trim().min(1).max(40),
23833
+ title: external_exports.string().trim().max(60).default(""),
23834
+ // An agent's own limits, so whatever's listed can be added.
23835
+ description: external_exports.string().trim().min(1, "Say what it's for.").max(120),
23836
+ instructions: external_exports.string().max(MAX_AGENT_INSTRUCTIONS).default(""),
23837
+ /** One of the drawn faces; or "custom", with the listing's picture. */
23838
+ avatar: external_exports.enum([...AGENT_AVATARS, "custom"])
23839
+ }),
23840
+ skills: external_exports.array(ListedSkillSchema).max(30).default([]),
23841
+ connectors: external_exports.array(ListedConnectorSchema).max(10).default([]),
23842
+ apps: external_exports.array(ListedAppSchema).max(3).default([]),
23843
+ workflows: external_exports.array(ListedWorkflowSchema).max(10).default([]),
23844
+ series: external_exports.array(ListedSeriesSchema).max(10).default([]),
23845
+ triggers: external_exports.array(ListedTriggerSchema).max(10).default([])
23846
+ }).superRefine((content, ctx) => {
23847
+ for (const problem of portabilityProblems(content))
23848
+ ctx.addIssue({ code: "custom", ...problem });
23849
+ });
23850
+ function portabilityProblems(content) {
23851
+ const problems = [];
23852
+ const keys = new Set(content.workflows.map((w) => w.key));
23853
+ const appTabs = new Set((content.apps ?? []).map(listedAppTab));
23854
+ const agent = (id, path) => {
23855
+ if (!AGENT_PLACEHOLDERS.includes(id)) {
23856
+ problems.push({ message: "It can only use this agent or Tabby.", path });
23857
+ }
23858
+ };
23859
+ const tab = (id, path) => {
23860
+ if (!TAB_PLACEHOLDERS.includes(id) && !appTabs.has(id)) {
23861
+ problems.push({ message: "It can only use a conversation, Docs or Tasks tab.", path });
23862
+ }
23863
+ };
23864
+ const person = (id, path) => {
23865
+ if (!ASSIGNEE_PLACEHOLDERS.includes(id)) {
23866
+ problems.push({ message: "It can only be for this agent, Tabby or whoever adds it.", path });
23867
+ }
23868
+ };
23869
+ const workflow = (key, path) => {
23870
+ if (key !== null && !keys.has(key)) {
23871
+ problems.push({ message: "It names a workflow the listing doesn't have.", path });
23872
+ }
23873
+ };
23874
+ content.workflows.forEach((w, i) => {
23875
+ if (content.workflows.findIndex((other) => other.key === w.key) !== i) {
23876
+ problems.push({ message: "Workflows' keys must differ.", path: ["workflows", i, "key"] });
23877
+ }
23878
+ for (const step of allSteps(w.steps)) {
23879
+ const at = ["workflows", i, "steps", step.id];
23880
+ switch (step.kind) {
23881
+ case "agent":
23882
+ agent(step.agentId, at);
23883
+ break;
23884
+ case "post":
23885
+ agent(step.agentId, at);
23886
+ tab(step.tabId, at);
23887
+ break;
23888
+ case "create_task":
23889
+ tab(step.tabId, at);
23890
+ step.assigneeIds.forEach((id) => person(id, at));
23891
+ break;
23892
+ case "person":
23893
+ person(step.assigneeId, at);
23894
+ break;
23895
+ case "if":
23896
+ if (step.condition.op === "agent") agent(step.condition.agentId, at);
23897
+ break;
23898
+ case "app_action":
23899
+ if (!appTabs.has(step.tabId)) {
23900
+ problems.push({ message: "Its actions need an app the listing brings.", path: at });
23901
+ }
23902
+ break;
23903
+ }
23904
+ }
23905
+ });
23906
+ content.series.forEach((s, i) => workflow(s.workflowKey, ["series", i, "workflowKey"]));
23907
+ content.triggers.forEach((t, i) => {
23908
+ const at = ["triggers", i, "event"];
23909
+ const event = t.event;
23910
+ if (event.kind === "app") {
23911
+ if (!appTabs.has(event.tabId)) {
23912
+ problems.push({ message: "Its events need an app the listing brings.", path: at });
23913
+ }
23914
+ } else if (event.kind === "inbox") {
23915
+ problems.push({ message: "A personal trigger (someone's Inbox) can't be listed.", path: at });
23916
+ } else if (event.kind !== "webhook") tab(event.tabId, at);
23917
+ if (event.kind === "message" && event.fromId !== null) person(event.fromId, at);
23918
+ workflow(t.task.workflowKey, ["triggers", i, "task", "workflowKey"]);
23919
+ });
23920
+ return problems;
23921
+ }
23922
+ var ListingPublisherSchema = external_exports.object({
23923
+ /** The workspace that publishes it, or Tabs & Spaces. */
23924
+ name: external_exports.string(),
23925
+ /** Made by Tabs & Spaces (decision 167): kept in our repository, listed as it changes. */
23926
+ official: external_exports.boolean().default(false)
23927
+ });
23928
+ var ListingNeedsSchema = external_exports.object({
23929
+ skills: external_exports.number().int().nonnegative(),
23930
+ connectors: external_exports.number().int().nonnegative(),
23931
+ workflows: external_exports.number().int().nonnegative(),
23932
+ recurring: external_exports.number().int().nonnegative(),
23933
+ triggers: external_exports.number().int().nonnegative(),
23934
+ tabs: external_exports.number().int().nonnegative().default(0),
23935
+ /** An agent's apps (decision 178). */
23936
+ apps: external_exports.number().int().nonnegative().default(0),
23937
+ actions: external_exports.number().int().nonnegative().default(0),
23938
+ events: external_exports.number().int().nonnegative().default(0)
23939
+ });
23940
+ var ListingSummarySchema = external_exports.object({
23941
+ id: external_exports.string(),
23942
+ slug: external_exports.string(),
23943
+ kind: ListingKindSchema,
23944
+ name: external_exports.string(),
23945
+ /** Its job title (agents), or a tagline. */
23946
+ title: external_exports.string(),
23947
+ description: external_exports.string(),
23948
+ categories: external_exports.array(ListingCategorySchema),
23949
+ avatar: external_exports.string(),
23950
+ /** Its own picture's address, if it has one. */
23951
+ pictureUrl: external_exports.string().nullable(),
23952
+ publisher: ListingPublisherSchema,
23953
+ version: external_exports.number().int().positive(),
23954
+ installs: external_exports.number().int().nonnegative(),
23955
+ needs: ListingNeedsSchema,
23956
+ publishedAt: external_exports.iso.datetime(),
23957
+ updatedAt: external_exports.iso.datetime()
23958
+ });
23959
+ var ListingSearchSchema = external_exports.object({
23960
+ listings: external_exports.array(ListingSummarySchema),
23961
+ total: external_exports.number().int().nonnegative()
23962
+ });
23963
+ var ListingImageViewSchema = external_exports.object({
23964
+ id: external_exports.string(),
23965
+ url: external_exports.string(),
23966
+ caption: external_exports.string()
23967
+ });
23968
+ var ListingPage = ListingSummarySchema.extend({
23969
+ /** A longer account of it, in Markdown. */
23970
+ about: external_exports.string(),
23971
+ /** What changed in this version. */
23972
+ changes: external_exports.string(),
23973
+ /** Screenshots and pictures of it at work (decision 202), in order. */
23974
+ images: external_exports.array(ListingImageViewSchema).default([])
23975
+ });
23976
+ var AgentListingSchema = ListingPage.extend({
23977
+ kind: external_exports.literal("agent"),
23978
+ content: AgentListingContentSchema
23979
+ });
23980
+ var AppListingSchema = ListingPage.extend({
23981
+ kind: external_exports.literal("app"),
23982
+ content: external_exports.lazy(() => AppListingContentSchema)
23983
+ });
23984
+ var ListingSchema = external_exports.discriminatedUnion("kind", [AgentListingSchema, AppListingSchema]);
23985
+ var ListingResponseSchema = external_exports.object({ listing: ListingSchema });
23986
+ var ListingPictureSchema = external_exports.object({
23987
+ type: external_exports.enum(["image/png", "image/jpeg", "image/webp"]),
23988
+ base64: external_exports.string().max(35e4, "The picture is too big (256 kB at most).").regex(/^[A-Za-z0-9+/]+=*$/)
23989
+ });
23990
+ var MAX_LISTING_IMAGES = 6;
23991
+ var MAX_LISTING_IMAGE_BYTES = 1048576;
23992
+ var ListingImageIdSchema = external_exports.string().regex(/^[0-9a-f]{64}$/, "An image's id.");
23993
+ var ImageCaption = external_exports.string().trim().max(140, "A caption is 140 characters at most.").default("");
23994
+ var ListingImageSchema = external_exports.union([
23995
+ external_exports.object({
23996
+ type: external_exports.enum(["image/png", "image/jpeg", "image/webp"]),
23997
+ base64: external_exports.string().max(Math.ceil(MAX_LISTING_IMAGE_BYTES / 3) * 4, "An image is 1 MB at most.").regex(/^[A-Za-z0-9+/]+=*$/),
23998
+ caption: ImageCaption
23999
+ }).strict(),
24000
+ external_exports.object({ id: ListingImageIdSchema, caption: ImageCaption }).strict()
24001
+ ]);
24002
+ var ListingImages = external_exports.array(ListingImageSchema).max(MAX_LISTING_IMAGES, `${MAX_LISTING_IMAGES} images at most.`).default([]);
24003
+ var PrivacyUrlSchema = external_exports.url({ protocol: /^https$/, message: "The privacy policy's address, starting https://." }).max(500);
24004
+ var AppListingContentSchema = external_exports.object({
24005
+ app: external_exports.object({
24006
+ /** Its manifest's `id`. */
24007
+ id: external_exports.string().min(3).max(64),
24008
+ name: external_exports.string().trim().min(1).max(40),
24009
+ /** A line about it. */
24010
+ title: external_exports.string().trim().max(60).default(""),
24011
+ description: external_exports.string().trim().min(1).max(300),
24012
+ // https, as installing requires (http only where a deployment allows private networks).
24013
+ manifestUrl: external_exports.url({ protocol: /^https?$/, message: "The manifest's address." }).max(500),
24014
+ /**
24015
+ * The manifest we reviewed (decision 203): installing from the listing refuses
24016
+ * another. Ours have none (we serve them, and change them as we deploy).
24017
+ */
24018
+ manifestHash: ManifestHashSchema.optional(),
24019
+ /**
24020
+ * Its developer's privacy policy (decision 205): what happens to the data
24021
+ * the app is sent. Every workspace's app has one; ours are on our site.
24022
+ */
24023
+ privacyUrl: PrivacyUrlSchema.optional()
24024
+ }),
24025
+ tabs: external_exports.array(
24026
+ external_exports.object({
24027
+ id: external_exports.string().max(40),
24028
+ name: external_exports.string().max(40),
24029
+ description: external_exports.string().max(200).default("")
24030
+ })
24031
+ ).min(1).max(10),
24032
+ actions: external_exports.array(
24033
+ external_exports.object({
24034
+ name: external_exports.string().max(40),
24035
+ description: external_exports.string().max(300),
24036
+ changes: external_exports.boolean()
24037
+ })
24038
+ ).max(20).default([]),
24039
+ events: external_exports.array(external_exports.object({ name: external_exports.string().max(40), description: external_exports.string().max(300) })).max(20).default([])
24040
+ });
24041
+ var DraftCommon = {
24042
+ categories: external_exports.array(ListingCategorySchema).min(1, "Choose a category.").max(3),
24043
+ about: external_exports.string().max(1e4).default(""),
24044
+ changes: external_exports.string().max(2e3).default(""),
24045
+ picture: ListingPictureSchema.nullable().default(null),
24046
+ images: ListingImages
24047
+ };
24048
+ var AgentListingDraftSchema = external_exports.object({
24049
+ kind: external_exports.literal("agent"),
24050
+ ...DraftCommon,
24051
+ content: AgentListingContentSchema
24052
+ });
24053
+ var ListingDraftSchema = external_exports.discriminatedUnion("kind", [
24054
+ AgentListingDraftSchema,
24055
+ external_exports.object({ kind: external_exports.literal("app"), ...DraftCommon, content: AppListingContentSchema })
24056
+ ]);
24057
+ var PublishedFrom = {
24058
+ /** Its listing, for a new version (one this workspace published). */
24059
+ listingId: external_exports.string().max(64).nullable().default(null),
24060
+ workspace: external_exports.object({ id: external_exports.string().min(1).max(64), name: external_exports.string().trim().min(1).max(120) }),
24061
+ /** Who sent it (we write to them about the review; never shown). */
24062
+ publishedBy: external_exports.object({ name: external_exports.string().trim().min(1).max(120), email: external_exports.email() }),
24063
+ /**
24064
+ * The publisher terms' version its sender accepted just now (decision 205;
24065
+ * null: the workspace accepted these terms before).
24066
+ */
24067
+ acceptedTerms: external_exports.string().max(20).nullable().default(null)
24068
+ };
24069
+ var ControlPublishAppSchema = external_exports.object({
24070
+ kind: external_exports.literal("app"),
24071
+ ...DraftCommon,
24072
+ manifestUrl: external_exports.url({ protocol: /^https?$/, message: "The manifest's address." }).max(500),
24073
+ manifestHash: ManifestHashSchema,
24074
+ privacyUrl: PrivacyUrlSchema,
24075
+ ...PublishedFrom
24076
+ });
24077
+ var ControlPublishListingSchema = external_exports.discriminatedUnion("kind", [
24078
+ AgentListingDraftSchema.extend(PublishedFrom),
24079
+ ControlPublishAppSchema
24080
+ ]);
24081
+ var ListingStatusSchema = external_exports.enum(["pending", "approved", "rejected", "removed"]);
24082
+ var PublishedListingSchema = external_exports.object({
24083
+ id: external_exports.string(),
24084
+ slug: external_exports.string(),
24085
+ kind: ListingKindSchema,
24086
+ name: external_exports.string(),
24087
+ /** The version everyone sees (null: none approved yet). */
24088
+ liveVersion: external_exports.number().int().positive().nullable(),
24089
+ /** The newest version and its review. */
24090
+ latest: external_exports.object({
24091
+ version: external_exports.number().int().positive(),
24092
+ status: external_exports.enum(["pending", "approved", "rejected"]),
24093
+ /** Why we didn't list it. */
24094
+ reason: external_exports.string().nullable(),
24095
+ submittedAt: external_exports.iso.datetime()
24096
+ }),
24097
+ /** Taken down by us (with why), or by the workspace. */
24098
+ removed: external_exports.object({ reason: external_exports.string(), at: external_exports.iso.datetime() }).nullable(),
24099
+ installs: external_exports.number().int().nonnegative()
24100
+ });
24101
+ var PublishedListingsSchema = external_exports.object({
24102
+ listings: external_exports.array(PublishedListingSchema),
24103
+ /**
24104
+ * The workspace's token (decision 203): an app's manifest names it in
24105
+ * `marketplace.verify` to say this workspace may publish it.
24106
+ */
24107
+ publisherToken: external_exports.string().nullable().default(null),
24108
+ /** The publisher terms (decision 205), and the version this workspace accepted. */
24109
+ terms: external_exports.object({ version: external_exports.string(), url: external_exports.string(), accepted: external_exports.string().nullable() }).nullable().default(null)
24110
+ });
24111
+ var PublisherTermsSchema = external_exports.object({
24112
+ version: external_exports.string(),
24113
+ url: external_exports.string(),
24114
+ /** This workspace accepted this version. */
24115
+ accepted: external_exports.boolean()
24116
+ });
24117
+ var ListingStatusRequestSchema = external_exports.object({
24118
+ ids: external_exports.array(external_exports.string().min(1).max(64)).min(1).max(100)
24119
+ });
24120
+ var ListingStatusesSchema = external_exports.object({
24121
+ listings: external_exports.array(
24122
+ external_exports.object({
24123
+ id: external_exports.string(),
24124
+ /** Live, for anyone to add. */
24125
+ listed: external_exports.boolean(),
24126
+ /** Taken down by Tabs & Spaces, and why (its workspace taking it out isn't this). */
24127
+ takenDown: external_exports.object({ reason: external_exports.string(), at: external_exports.iso.datetime() }).nullable()
24128
+ })
24129
+ )
24130
+ });
24131
+ var PublishingItemSchema = external_exports.object({
24132
+ id: external_exports.string(),
24133
+ name: external_exports.string(),
24134
+ /** A line about it ("ClawHub: @acme/notes", "Every Friday at 16:00"). */
24135
+ detail: external_exports.string(),
24136
+ /** Ticked to start with. */
24137
+ suggested: external_exports.boolean(),
24138
+ /** Why it can't be listed (empty: it can). */
24139
+ problems: external_exports.array(external_exports.string())
24140
+ });
24141
+ var AgentPublishingSchema = external_exports.object({
24142
+ /** This deployment is registered with Tabs & Spaces (publishing needs it). */
24143
+ registered: external_exports.boolean(),
24144
+ /** The agent's listing, if it was published. */
24145
+ listing: PublishedListingSchema.nullable(),
24146
+ /** Its live version's words, to start a new version from. */
24147
+ previous: external_exports.object({
24148
+ categories: external_exports.array(ListingCategorySchema),
24149
+ about: external_exports.string(),
24150
+ /** Its images, kept by id unless they're taken out (decision 202). */
24151
+ images: external_exports.array(ListingImageViewSchema).default([])
24152
+ }).nullable(),
24153
+ /** Where the marketplace shows a listing (its slug follows). */
24154
+ listingBase: external_exports.string(),
24155
+ /** The publisher terms (null: not registered, or unknown). */
24156
+ terms: PublisherTermsSchema.nullable().default(null),
24157
+ skills: external_exports.array(PublishingItemSchema),
24158
+ connectors: external_exports.array(PublishingItemSchema),
24159
+ /** Spaces where it has workflows, recurring tasks or triggers. */
24160
+ spaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })),
24161
+ /** The space whose work is shown below (null: none). */
24162
+ spaceId: external_exports.string().nullable(),
24163
+ workflows: external_exports.array(PublishingItemSchema),
24164
+ series: external_exports.array(PublishingItemSchema),
24165
+ triggers: external_exports.array(PublishingItemSchema)
24166
+ });
24167
+ var PublishAgentRequestSchema = external_exports.object({
24168
+ categories: external_exports.array(ListingCategorySchema).min(1, "Choose a category.").max(3),
24169
+ about: external_exports.string().max(1e4).default(""),
24170
+ changes: external_exports.string().max(2e3).default(""),
24171
+ spaceId: external_exports.string().max(64).nullable().default(null),
24172
+ skills: external_exports.array(external_exports.string().max(200)).max(30).default([]),
24173
+ connectors: external_exports.array(external_exports.string().max(64)).max(10).default([]),
24174
+ workflows: external_exports.array(external_exports.string().max(64)).max(10).default([]),
24175
+ series: external_exports.array(external_exports.string().max(64)).max(10).default([]),
24176
+ triggers: external_exports.array(external_exports.string().max(64)).max(10).default([]),
24177
+ images: ListingImages,
24178
+ /** The publisher terms' version accepted now (decision 205; left out: accepted before). */
24179
+ acceptTerms: external_exports.string().max(20).optional()
24180
+ }).strict();
24181
+ var PublishedListingResponseSchema = external_exports.object({ listing: PublishedListingSchema });
24182
+ var AppPublishingSchema = external_exports.object({
24183
+ /** This deployment is registered with Tabs & Spaces (publishing needs it). */
24184
+ registered: external_exports.boolean(),
24185
+ /** The app's listing, if this workspace published it. */
24186
+ listing: PublishedListingSchema.nullable(),
24187
+ /** Its live version's words and images, to start a new version from. */
24188
+ previous: external_exports.object({
24189
+ categories: external_exports.array(ListingCategorySchema),
24190
+ about: external_exports.string(),
24191
+ images: external_exports.array(ListingImageViewSchema).default([]),
24192
+ privacyUrl: external_exports.string().nullable().default(null)
24193
+ }).nullable(),
24194
+ listingBase: external_exports.string(),
24195
+ terms: PublisherTermsSchema.nullable().default(null),
24196
+ /** What its manifest says to name this workspace (null: not registered, or unknown). */
24197
+ publisherToken: external_exports.string().nullable(),
24198
+ /** The installed manifest names it. */
24199
+ verified: external_exports.boolean(),
24200
+ /** Why it can't be sent yet, as the installed manifest stands. */
24201
+ problems: external_exports.array(external_exports.string())
24202
+ });
24203
+ var PublishAppRequestSchema = external_exports.object({
24204
+ categories: external_exports.array(ListingCategorySchema).min(1, "Choose a category.").max(3),
24205
+ about: external_exports.string().max(1e4).default(""),
24206
+ changes: external_exports.string().max(2e3).default(""),
24207
+ images: ListingImages,
24208
+ privacyUrl: PrivacyUrlSchema,
24209
+ acceptTerms: external_exports.string().max(20).optional()
24210
+ }).strict();
24211
+ var PublishingKeySchema = external_exports.object({
24212
+ id: external_exports.string(),
24213
+ /** Its first characters, to tell keys apart (the rest is never shown again). */
24214
+ prefix: external_exports.string(),
24215
+ createdBy: external_exports.object({ name: external_exports.string() }),
24216
+ createdAt: external_exports.iso.datetime(),
24217
+ lastUsedAt: external_exports.iso.datetime().nullable()
24218
+ });
24219
+ var PublishingKeysSchema = external_exports.object({ keys: external_exports.array(PublishingKeySchema) });
24220
+ var CreatedPublishingKeySchema = external_exports.object({
24221
+ key: PublishingKeySchema,
24222
+ /** Shown once: `tands publish` sends it (TANDS_PUBLISH_KEY). */
24223
+ secret: external_exports.string()
24224
+ });
24225
+ var KeyPublishRequestSchema = external_exports.object({ changes: external_exports.string().max(2e3).default("") }).strict();
24226
+ var KeyPublishResultSchema = external_exports.object({
24227
+ app: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
24228
+ listing: PublishedListingSchema,
24229
+ /** The installed manifest changed (checked for updates first). */
24230
+ updated: external_exports.boolean()
24231
+ });
24232
+ var MarketplaceBrowseSchema = external_exports.object({
24233
+ /** Off: its deployment's admins turned the marketplace off. */
24234
+ enabled: external_exports.boolean(),
24235
+ listings: external_exports.array(ListingSummarySchema),
24236
+ total: external_exports.number().int().nonnegative(),
24237
+ /** Listings this workspace has added: listing id → its agent. */
24238
+ added: external_exports.record(external_exports.string(), external_exports.string()),
24239
+ /** Workspace owners and admins add them (skills and connectors are theirs to set up). */
24240
+ canAdd: external_exports.boolean()
24241
+ });
24242
+ var MarketplaceListingSchema = external_exports.object({
24243
+ listing: ListingSchema,
24244
+ /** The agent it was added as, if it was. */
24245
+ addedAs: external_exports.string().nullable(),
24246
+ /** What the workspace has already (it's shared, not added twice). */
24247
+ has: external_exports.object({ skills: external_exports.array(external_exports.string()), connectors: external_exports.array(external_exports.string()) }),
24248
+ /**
24249
+ * How each of its connectors signs in here (by its place in the listing):
24250
+ * a known server (GitHub) uses OAuth where this deployment registered its
24251
+ * app, else each person's own key in `headerName` (decision 167).
24252
+ */
24253
+ connectorAuth: external_exports.array(
24254
+ external_exports.object({
24255
+ auth: external_exports.enum(["none", "header", "oauth"]),
24256
+ headerName: external_exports.string().nullable(),
24257
+ keyHint: external_exports.string().nullable()
24258
+ })
24259
+ ).default([]),
24260
+ canAdd: external_exports.boolean()
24261
+ });
24262
+ var AddListingRequestSchema = external_exports.object({
24263
+ /** Where its work goes (needed when it has some). */
24264
+ spaceId: external_exports.string().max(64).nullable().default(null),
24265
+ /** Its recurring tasks' time zone: the adder's. */
24266
+ timeZone: external_exports.string().min(1).max(64),
24267
+ /** Keys for its connectors that sign in with one, by the connector's place in the listing. */
24268
+ keys: external_exports.record(external_exports.string().regex(/^\d{1,2}$/), external_exports.string().trim().min(1).max(4e3)).default({})
24269
+ }).strict();
24270
+ var AddedStepSchema = external_exports.object({
24271
+ what: external_exports.enum([
24272
+ "agent",
24273
+ "picture",
24274
+ "skill",
24275
+ "connector",
24276
+ "space",
24277
+ "workflow",
24278
+ "series",
24279
+ "trigger",
24280
+ "app",
24281
+ "tab"
24282
+ ]),
24283
+ name: external_exports.string(),
24284
+ ok: external_exports.boolean(),
24285
+ /** Why it didn't work (it can be done by hand). */
24286
+ message: external_exports.string().nullable()
24287
+ });
24288
+ var AddListingResultSchema = external_exports.object({
24289
+ /** The agent it was added as. */
24290
+ agent: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
24291
+ steps: external_exports.array(AddedStepSchema),
24292
+ /** Connectors waiting for an OAuth sign-in. */
24293
+ signIns: external_exports.array(external_exports.object({ connectorId: external_exports.string(), name: external_exports.string() }))
24294
+ });
24295
+ var AddAppListingRequestSchema = external_exports.object({ spaceId: external_exports.string().max(64).nullable().default(null) }).strict();
24296
+ var AddAppListingResultSchema = external_exports.object({
24297
+ /** The app, installed (or updated, if it was). */
24298
+ app: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
24299
+ /** Its tab, where it was added. */
24300
+ tab: external_exports.object({ spaceId: external_exports.string(), tabId: external_exports.string() }).nullable(),
24301
+ steps: external_exports.array(AddedStepSchema)
24302
+ });
24303
+
23222
24304
  // src/harness.ts
23223
24305
  import { createHash, generateKeyPairSync, randomBytes, sign } from "node:crypto";
23224
24306
  import { createServer } from "node:http";
@@ -23254,6 +24336,7 @@ function checkManifest(json2, url2) {
23254
24336
  manifest2.tabs.forEach((tab) => onOrigin(tab.url, `tabs.${tab.id}.url`));
23255
24337
  for (const [name, action] of Object.entries(manifest2.actions ?? {})) {
23256
24338
  onOrigin(action.url, `actions.${name}.url`);
24339
+ if (action.view) onOrigin(action.view.url, `actions.${name}.view.url`);
23257
24340
  }
23258
24341
  if (manifest2.icon) onOrigin(manifest2.icon, "icon");
23259
24342
  }
@@ -23329,6 +24412,7 @@ function harnessPage() {
23329
24412
  input, select, textarea { width: 100%; padding: 7px 9px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); }
23330
24413
  button { padding: 7px 12px; border-radius: 8px; border: 1px solid var(--border); background: var(--canvas); cursor: pointer; }
23331
24414
  button.primary { background: var(--accent); color: var(--canvas); border-color: var(--accent); }
24415
+ button[aria-pressed="true"] { background: var(--text); color: var(--canvas); border-color: var(--text); }
23332
24416
  .frame { display: flex; flex-direction: column; }
23333
24417
  .frame .bar { display: flex; align-items: center; gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--border); color: var(--muted); font-size: 12px; }
23334
24418
  .frame .bar code { color: var(--text); }
@@ -23352,6 +24436,7 @@ function harnessPage() {
23352
24436
  <strong>App harness</strong><span class="muted" id="appName"></span>
23353
24437
  <span class="spacer"></span>
23354
24438
  <button type="button" id="theme">Dark</button>
24439
+ <button type="button" id="readOnly" aria-pressed="false" title="As when its bill is unpaid: reads work, writes are refused">Read-only</button>
23355
24440
  <button type="button" id="reset">Clear data</button>
23356
24441
  </header>
23357
24442
  <div id="problems" class="problems" hidden></div>
@@ -23395,6 +24480,8 @@ function harnessPage() {
23395
24480
  const $ = (id) => document.getElementById(id);
23396
24481
  const frame = $("frame");
23397
24482
  let theme = "light";
24483
+ // The workspace read-only (an unpaid bill): the page is told, and writes are refused.
24484
+ let readOnly = false;
23398
24485
  let info = null;
23399
24486
  let origin = null;
23400
24487
 
@@ -23402,7 +24489,7 @@ const post = async (path, body) => {
23402
24489
  const response = await fetch(path, { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) });
23403
24490
  return response.json();
23404
24491
  };
23405
- const who = () => Object.fromEntries(new FormData($("who")));
24492
+ const who = () => ({ ...Object.fromEntries(new FormData($("who"))), readOnly });
23406
24493
  const send = (message) => frame.contentWindow?.postMessage({ tands: 1, ...message }, origin);
23407
24494
  const reply = (id, out) =>
23408
24495
  out && out.error ? send({ type: "error", id, code: out.error.code, message: out.error.message }) : send({ type: "data", id, result: out.result });
@@ -23505,6 +24592,11 @@ $("theme").addEventListener("click", () => {
23505
24592
  $("theme").textContent = theme === "light" ? "Dark" : "Light";
23506
24593
  send({ type: "theme", theme });
23507
24594
  });
24595
+ $("readOnly").addEventListener("click", () => {
24596
+ readOnly = !readOnly;
24597
+ $("readOnly").setAttribute("aria-pressed", String(readOnly));
24598
+ send({ type: "readOnly", readOnly });
24599
+ });
23508
24600
  $("reset").addEventListener("click", async () => { await post("/harness/reset", {}); void refresh(); });
23509
24601
  void load().then(refresh);
23510
24602
  setInterval(refresh, 2000);
@@ -23674,6 +24766,9 @@ function createMemoryStore() {
23674
24766
  }
23675
24767
 
23676
24768
  // src/harness.ts
24769
+ var READING = /* @__PURE__ */ new Set(["values.get", "values.list", "records.list", "records.get"]);
24770
+ var READ_ONLY_MESSAGE = "This workspace is read-only until its bill is paid.";
24771
+ var writes = (request) => !READING.has(String(request?.op));
23677
24772
  var ACTION_TOKEN_SECONDS = 120;
23678
24773
  var DATA_KEY_MS = 5 * 6e4;
23679
24774
  var ACTION_TIMEOUT_MS = 1e4;
@@ -23722,7 +24817,12 @@ async function startHarness({
23722
24817
  const spaceId = idFrom("s", who.spaceName || "Design");
23723
24818
  return {
23724
24819
  user: { id: idFrom("u", who.name || "Ada Lovelace"), name: who.name || "Ada Lovelace" },
23725
- workspace: { id: "dev-workspace", slug: "dev", name: "Harness workspace" },
24820
+ workspace: {
24821
+ id: "dev-workspace",
24822
+ slug: "dev",
24823
+ name: "Harness workspace",
24824
+ readOnly: who.readOnly === true
24825
+ },
23726
24826
  space: { id: spaceId, kind: who.spaceKind, name: who.spaceName || "Design" },
23727
24827
  tab: { id: `${spaceId}-${who.appTabId}`, appTabId: who.appTabId },
23728
24828
  app: { id: manifest2.id },
@@ -23757,7 +24857,7 @@ async function startHarness({
23757
24857
  }
23758
24858
  const isWho = (value) => {
23759
24859
  const who = value;
23760
- return typeof who?.name === "string" && typeof who.spaceName === "string" && typeof who.locale === "string" && typeof who.appTabId === "string" && ["normal", "general", "personal", "dm"].includes(String(who.spaceKind));
24860
+ return typeof who?.name === "string" && typeof who.spaceName === "string" && typeof who.locale === "string" && typeof who.appTabId === "string" && (who.readOnly === void 0 || typeof who.readOnly === "boolean") && ["normal", "general", "personal", "dm"].includes(String(who.spaceKind));
23761
24861
  };
23762
24862
  function logEvent(loaded, body, from, person) {
23763
24863
  const manifest2 = loaded.manifest;
@@ -23887,6 +24987,9 @@ async function startHarness({
23887
24987
  }
23888
24988
  const loaded2 = await current();
23889
24989
  if (!loaded2.manifest) return refused2(409, "BAD_MANIFEST", loaded2.problems.join(" "));
24990
+ if (claims.who.readOnly && (url2.pathname === "/api/apps/events" || writes(body))) {
24991
+ return refused2(423, "WORKSPACE_READ_ONLY", READ_ONLY_MESSAGE);
24992
+ }
23890
24993
  if (url2.pathname === "/api/apps/events") {
23891
24994
  const outcome2 = logEvent(loaded2, body, "server", claims.who.name);
23892
24995
  return "error" in outcome2 ? send(outcome2.status, { error: outcome2.error }) : send(200, { result: outcome2.result });
@@ -23903,6 +25006,9 @@ async function startHarness({
23903
25006
  return refused2(404, "NOT_FOUND", `${manifest2.name} has no tab \u201C${who.appTabId}\u201D.`);
23904
25007
  }
23905
25008
  const context = contextFor(who, manifest2);
25009
+ if (who.readOnly && (url2.pathname === "/harness/event" || url2.pathname === "/harness/data" && writes(body.request))) {
25010
+ return refused2(423, "WORKSPACE_READ_ONLY", READ_ONLY_MESSAGE);
25011
+ }
23906
25012
  switch (url2.pathname) {
23907
25013
  case "/harness/token": {
23908
25014
  const tab = manifest2.tabs.find((one) => one.id === who.appTabId);
@@ -23948,6 +25054,49 @@ async function startHarness({
23948
25054
  };
23949
25055
  }
23950
25056
 
25057
+ // src/publish.ts
25058
+ async function publish({
25059
+ url: url2,
25060
+ key,
25061
+ changes = "",
25062
+ fetchImpl = fetch
25063
+ }) {
25064
+ let origin;
25065
+ try {
25066
+ origin = new URL(url2).origin;
25067
+ } catch {
25068
+ return { ok: false, message: `\u201C${url2}\u201D isn't a web address (TANDS_URL).` };
25069
+ }
25070
+ let response;
25071
+ try {
25072
+ response = await fetchImpl(`${origin}/api/publish/app`, {
25073
+ method: "POST",
25074
+ headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
25075
+ body: JSON.stringify({ changes }),
25076
+ signal: AbortSignal.timeout(6e4)
25077
+ });
25078
+ } catch (error62) {
25079
+ return {
25080
+ ok: false,
25081
+ message: `${origin} can't be reached: ${error62 instanceof Error ? error62.message : String(error62)}`
25082
+ };
25083
+ }
25084
+ const json2 = await response.json().catch(() => null);
25085
+ if (!response.ok || !json2?.app || !json2.listing) {
25086
+ return {
25087
+ ok: false,
25088
+ message: json2?.error?.message ?? `${origin} answered HTTP ${response.status}.`
25089
+ };
25090
+ }
25091
+ return {
25092
+ ok: true,
25093
+ app: json2.app,
25094
+ version: json2.listing.latest.version,
25095
+ live: json2.listing.liveVersion,
25096
+ updated: Boolean(json2.updated)
25097
+ };
25098
+ }
25099
+
23951
25100
  // src/scaffold.ts
23952
25101
  import { existsSync } from "node:fs";
23953
25102
  import { copyFile, mkdir, readdir, writeFile } from "node:fs/promises";
@@ -24212,7 +25361,7 @@ your manifest's address. See https://developer.tabsandspaces.dev.
24212
25361
  `;
24213
25362
 
24214
25363
  // src/bin.ts
24215
- var version2 = true ? "0.1.0" : "dev";
25364
+ var version2 = true ? "0.3.0" : "dev";
24216
25365
  var USAGE = `tands: developer tools for Tabs & Spaces apps
24217
25366
 
24218
25367
  tands new <folder> [--name "My app"] [--id my-app]
@@ -24227,13 +25376,21 @@ var USAGE = `tands: developer tools for Tabs & Spaces apps
24227
25376
  choose, with your data in memory, your events and your actions.
24228
25377
  Default: http://localhost:5480/tands.json
24229
25378
 
25379
+ tands publish [--changes "What changed"]
25380
+ Sends a new version of your app to the marketplace for review. Its
25381
+ workspace checks it for updates first, then sends it with its listing's
25382
+ words and images. Needs TANDS_URL (the workspace's address) and
25383
+ TANDS_PUBLISH_KEY (a key from Settings \u2192 Apps \u2192 Publish\u2026 there).
25384
+
24230
25385
  tands --version
24231
25386
 
24232
25387
  Docs: https://developer.tabsandspaces.dev`;
24233
25388
  async function main(argv, {
24234
25389
  cwd = process.env.INIT_CWD ?? process.cwd(),
24235
- out = (line) => console.log(line),
24236
- err = (line) => console.error(line)
25390
+ out = (line2) => console.log(line2),
25391
+ err = (line2) => console.error(line2),
25392
+ env = process.env,
25393
+ fetchImpl = fetch
24237
25394
  } = {}) {
24238
25395
  const { values, positionals } = parseArgs({
24239
25396
  args: argv,
@@ -24242,6 +25399,7 @@ async function main(argv, {
24242
25399
  name: { type: "string" },
24243
25400
  id: { type: "string" },
24244
25401
  port: { type: "string" },
25402
+ changes: { type: "string" },
24245
25403
  help: { type: "boolean", short: "h" },
24246
25404
  version: { type: "boolean", short: "v" }
24247
25405
  }
@@ -24334,6 +25492,29 @@ async function main(argv, {
24334
25492
  return 1;
24335
25493
  }
24336
25494
  }
25495
+ case "publish": {
25496
+ const url2 = env.TANDS_URL?.trim();
25497
+ const key = env.TANDS_PUBLISH_KEY?.trim();
25498
+ if (!url2 || !key) {
25499
+ err("tands publish needs your workspace's address and a publishing key:");
25500
+ err(" TANDS_URL=https://your.workspace TANDS_PUBLISH_KEY=tas-pk-\u2026 npx tands publish");
25501
+ err("Make the key in the workspace: Settings \u2192 Apps \u2192 Publish\u2026 \u2192 Make a key.");
25502
+ return 1;
25503
+ }
25504
+ const sent = await publish({ url: url2, key, changes: values.changes ?? "", fetchImpl });
25505
+ if (!sent.ok) {
25506
+ err(sent.message);
25507
+ return 1;
25508
+ }
25509
+ out(
25510
+ `\u2713 Sent ${sent.app.name} (${sent.app.id}) version ${sent.version} to the marketplace for review.`
25511
+ );
25512
+ if (sent.updated) out(" Its workspace had an older manifest: it's up to date now.");
25513
+ out(
25514
+ sent.live ? ` Version ${sent.live} stays listed until it's reviewed; you'll get an email then.` : " You'll get an email when it's reviewed."
25515
+ );
25516
+ return 0;
25517
+ }
24337
25518
  default:
24338
25519
  err(`There's no command \u201C${command}\u201D.`);
24339
25520
  out(USAGE);