tands 0.1.0 → 0.2.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/README.md +2 -0
- package/dist/sdk/tands-sdk-server.js +19 -0
- package/dist/sdk/tands-sdk.js +36 -0
- package/dist/tands.js +884 -63
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -13,6 +13,8 @@ npx tands dev # a local workspace for it, on http://localho
|
|
|
13
13
|
- **`tands check [manifest]`** checks a manifest (a file, or its address) as a workspace would.
|
|
14
14
|
- **`tands dev [manifest address]`** stands in for a workspace: your tab as someone you choose,
|
|
15
15
|
real tokens signed with its own keys, your data in memory, your events and your actions.
|
|
16
|
+
- **`tands publish [--changes "…"]`** sends a new version of your listed app to the marketplace
|
|
17
|
+
for review, with a publishing key from its workspace (`TANDS_URL`, `TANDS_PUBLISH_KEY`).
|
|
16
18
|
|
|
17
19
|
For your own build, the SDK is [`@tabs-and-spaces/sdk`](https://www.npmjs.com/package/@tabs-and-spaces/sdk).
|
|
18
20
|
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
// ../sdk/src/protocol.ts
|
|
2
|
+
var isHome = (context) => Boolean(context.home);
|
|
3
|
+
|
|
1
4
|
// ../sdk/src/server.ts
|
|
2
5
|
var TokenError = class extends Error {
|
|
3
6
|
};
|
|
@@ -121,6 +124,20 @@ function dataClient({ dataKey, dataUrl }, fetcher = fetch) {
|
|
|
121
124
|
}
|
|
122
125
|
};
|
|
123
126
|
}
|
|
127
|
+
async function reportEvent({ key, eventsUrl }, name, fields = {}, fetcher = fetch) {
|
|
128
|
+
const response = await fetcher(eventsUrl, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers: { "content-type": "application/json", authorization: `Bearer ${key}` },
|
|
131
|
+
body: JSON.stringify({ name, data: fields })
|
|
132
|
+
});
|
|
133
|
+
if (!response.ok) {
|
|
134
|
+
const body = await response.json().catch(() => null);
|
|
135
|
+
throw new DataError(
|
|
136
|
+
body?.error?.message ?? `It answered ${response.status}.`,
|
|
137
|
+
body?.error?.code ?? null
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
124
141
|
var DataError = class extends Error {
|
|
125
142
|
constructor(message, code) {
|
|
126
143
|
super(message);
|
|
@@ -133,5 +150,7 @@ export {
|
|
|
133
150
|
TokenError,
|
|
134
151
|
dataClient,
|
|
135
152
|
fetchKeys,
|
|
153
|
+
isHome,
|
|
154
|
+
reportEvent,
|
|
136
155
|
verifyToken
|
|
137
156
|
};
|
package/dist/sdk/tands-sdk.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
// ../sdk/src/protocol.ts
|
|
2
2
|
var PROTOCOL_VERSION = 1;
|
|
3
|
+
var isHome = (context) => Boolean(context.home);
|
|
3
4
|
var isRecord = (value) => typeof value === "object" && value !== null;
|
|
4
5
|
function readAppMessage(data) {
|
|
5
6
|
if (!isRecord(data) || data.tands !== PROTOCOL_VERSION) return null;
|
|
@@ -10,6 +11,27 @@ function readAppMessage(data) {
|
|
|
10
11
|
if (data.type === "data" && typeof data.id === "string" && data.id.length <= 64 && isRecord(data.request) && typeof data.request.op === "string") {
|
|
11
12
|
return { tands: 1, type: "data", id: data.id, request: data.request };
|
|
12
13
|
}
|
|
14
|
+
if (data.type === "eventKey" && typeof data.id === "string" && data.id.length <= 64) {
|
|
15
|
+
return { tands: 1, type: "eventKey", id: data.id };
|
|
16
|
+
}
|
|
17
|
+
if (data.type === "openTrigger" && typeof data.id === "string" && data.id.length <= 64 && (data.event === void 0 || data.event === null || typeof data.event === "string" && data.event.length <= 40)) {
|
|
18
|
+
return {
|
|
19
|
+
tands: 1,
|
|
20
|
+
type: "openTrigger",
|
|
21
|
+
id: data.id,
|
|
22
|
+
event: typeof data.event === "string" ? data.event : null
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
const id = (value) => typeof value === "string" && value.length > 0 && value.length <= 64;
|
|
26
|
+
if (data.type === "open" && id(data.id) && id(data.space) && (data.tab === void 0 || data.tab === null || id(data.tab))) {
|
|
27
|
+
return {
|
|
28
|
+
tands: 1,
|
|
29
|
+
type: "open",
|
|
30
|
+
id: data.id,
|
|
31
|
+
space: data.space,
|
|
32
|
+
tab: typeof data.tab === "string" ? data.tab : null
|
|
33
|
+
};
|
|
34
|
+
}
|
|
13
35
|
if (data.type === "event" && typeof data.id === "string" && data.id.length <= 64 && typeof data.name === "string" && data.name.length <= 40 && (data.data === void 0 || isRecord(data.data))) {
|
|
14
36
|
return {
|
|
15
37
|
tands: 1,
|
|
@@ -155,6 +177,19 @@ function connect({ timeoutMs = 1e4, window: win = window } = {}) {
|
|
|
155
177
|
async emit(name, fields = {}) {
|
|
156
178
|
await ask((id) => ({ tands: 1, type: "event", id, name, data: fields }));
|
|
157
179
|
},
|
|
180
|
+
requestEventKey: () => ask((id) => ({ tands: 1, type: "eventKey", id })),
|
|
181
|
+
async open(target) {
|
|
182
|
+
await ask((id) => ({
|
|
183
|
+
tands: 1,
|
|
184
|
+
type: "open",
|
|
185
|
+
id,
|
|
186
|
+
space: target.space,
|
|
187
|
+
tab: target.tab ?? null
|
|
188
|
+
}));
|
|
189
|
+
},
|
|
190
|
+
async openTrigger(event) {
|
|
191
|
+
await ask((id) => ({ tands: 1, type: "openTrigger", id, event: event ?? null }));
|
|
192
|
+
},
|
|
158
193
|
values(scope) {
|
|
159
194
|
return {
|
|
160
195
|
get: async (key) => (await data({ op: "values.get", scope, key })).value,
|
|
@@ -198,6 +233,7 @@ export {
|
|
|
198
233
|
PROTOCOL_VERSION,
|
|
199
234
|
WorkspaceError,
|
|
200
235
|
connect,
|
|
236
|
+
isHome,
|
|
201
237
|
readAppMessage,
|
|
202
238
|
readHostMessage
|
|
203
239
|
};
|
package/dist/tands.js
CHANGED
|
@@ -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;
|
|
@@ -19751,6 +19843,18 @@ var AppManifestSchema = external_exports.object({
|
|
|
19751
19843
|
url: external_exports.string().min(1).max(500)
|
|
19752
19844
|
})
|
|
19753
19845
|
).min(1, "An app needs at least one tab.").max(MAX_APP_TABS),
|
|
19846
|
+
/**
|
|
19847
|
+
* Home pages it offers (decision 177): an owner or admin may choose one as the
|
|
19848
|
+
* workspace's Home. Shown like a tab, without a space around it.
|
|
19849
|
+
*/
|
|
19850
|
+
home: external_exports.array(
|
|
19851
|
+
external_exports.object({
|
|
19852
|
+
id: AppTabIdSchema,
|
|
19853
|
+
name: external_exports.string().trim().min(1).max(40),
|
|
19854
|
+
description: external_exports.string().trim().max(200).default(""),
|
|
19855
|
+
url: external_exports.string().min(1).max(500)
|
|
19856
|
+
})
|
|
19857
|
+
).max(MAX_APP_HOME_PAGES).optional(),
|
|
19754
19858
|
/** The data it keeps in the workspace (M9 slice 2). */
|
|
19755
19859
|
data: external_exports.object({
|
|
19756
19860
|
collections: external_exports.record(DataNameSchema, AppCollectionSchema).refine(
|
|
@@ -19761,8 +19865,15 @@ var AppManifestSchema = external_exports.object({
|
|
|
19761
19865
|
/** What it does when agents ask (M9 slice 3). */
|
|
19762
19866
|
actions: external_exports.record(DataNameSchema, AppActionSchema).refine((actions) => Object.keys(actions).length <= 20, "An app has at most 20 actions.").optional(),
|
|
19763
19867
|
/** 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()
|
|
19868
|
+
events: external_exports.record(DataNameSchema, AppEventSchema).refine((events) => Object.keys(events).length <= 20, "An app has at most 20 events.").optional(),
|
|
19869
|
+
/**
|
|
19870
|
+
* Publishing it to the marketplace (decision 203): `verify` is the token of
|
|
19871
|
+
* the workspace that may publish it, shown on its Publish page. Only whoever
|
|
19872
|
+
* controls the manifest's address can put it there.
|
|
19873
|
+
*/
|
|
19874
|
+
marketplace: external_exports.object({ verify: external_exports.string().trim().max(100) }).optional()
|
|
19765
19875
|
});
|
|
19876
|
+
var ManifestHashSchema = external_exports.string().regex(/^[0-9a-f]{64}$/, "A manifest's hash.");
|
|
19766
19877
|
var AppActionInfoSchema = external_exports.object({
|
|
19767
19878
|
name: external_exports.string(),
|
|
19768
19879
|
description: external_exports.string(),
|
|
@@ -19823,6 +19934,8 @@ var AppSchema = external_exports.object({
|
|
|
19823
19934
|
origin: external_exports.string(),
|
|
19824
19935
|
manifestUrl: external_exports.string(),
|
|
19825
19936
|
tabs: external_exports.array(AppTabSchema),
|
|
19937
|
+
/** Home pages it offers (decision 177). */
|
|
19938
|
+
homePages: external_exports.array(AppTabSchema).default([]),
|
|
19826
19939
|
/** The data it keeps (M9 slice 2). */
|
|
19827
19940
|
collections: external_exports.array(AppCollectionInfoSchema),
|
|
19828
19941
|
/** What it does when agents ask (M9 slice 3). */
|
|
@@ -19834,7 +19947,14 @@ var AppSchema = external_exports.object({
|
|
|
19834
19947
|
dataLimitBytes: external_exports.number(),
|
|
19835
19948
|
installedBy: external_exports.object({ userId: external_exports.string(), name: external_exports.string() }),
|
|
19836
19949
|
installedAt: external_exports.string(),
|
|
19837
|
-
updatedAt: external_exports.string()
|
|
19950
|
+
updatedAt: external_exports.string(),
|
|
19951
|
+
/** The marketplace listing it was installed from (decision 205; null: by its address). */
|
|
19952
|
+
fromListing: external_exports.object({ id: external_exports.string(), slug: external_exports.string() }).nullable().default(null),
|
|
19953
|
+
/**
|
|
19954
|
+
* Tabs & Spaces took that listing down (decision 205), and why: it still
|
|
19955
|
+
* works here, for the workspace's owners and admins to decide.
|
|
19956
|
+
*/
|
|
19957
|
+
takenDown: external_exports.object({ reason: external_exports.string(), at: external_exports.string() }).nullable().default(null)
|
|
19838
19958
|
});
|
|
19839
19959
|
var AppListSchema = external_exports.object({
|
|
19840
19960
|
apps: external_exports.array(AppSchema),
|
|
@@ -19843,7 +19963,9 @@ var AppListSchema = external_exports.object({
|
|
|
19843
19963
|
});
|
|
19844
19964
|
var AppResponseSchema = external_exports.object({ app: AppSchema });
|
|
19845
19965
|
var InstallAppRequestSchema = external_exports.object({
|
|
19846
|
-
manifestUrl: external_exports.url({ protocol: /^https?$/, error: "Enter the manifest's full address." })
|
|
19966
|
+
manifestUrl: external_exports.url({ protocol: /^https?$/, error: "Enter the manifest's full address." }),
|
|
19967
|
+
/** From a listing (decision 203): only the manifest it reviewed, by its hash. */
|
|
19968
|
+
manifestHash: ManifestHashSchema.optional()
|
|
19847
19969
|
});
|
|
19848
19970
|
var AppPreviewSchema = external_exports.object({
|
|
19849
19971
|
app: AppSchema.omit({
|
|
@@ -19852,7 +19974,9 @@ var AppPreviewSchema = external_exports.object({
|
|
|
19852
19974
|
updatedAt: true,
|
|
19853
19975
|
iconUrl: true,
|
|
19854
19976
|
dataBytes: true,
|
|
19855
|
-
dataLimitBytes: true
|
|
19977
|
+
dataLimitBytes: true,
|
|
19978
|
+
fromListing: true,
|
|
19979
|
+
takenDown: true
|
|
19856
19980
|
}),
|
|
19857
19981
|
/** Its picture, inline (it isn't kept until installed). */
|
|
19858
19982
|
icon: external_exports.string().nullable(),
|
|
@@ -19876,11 +20000,30 @@ var AppContextSchema = external_exports.object({
|
|
|
19876
20000
|
app: external_exports.object({ id: external_exports.string() }),
|
|
19877
20001
|
locale: external_exports.string()
|
|
19878
20002
|
});
|
|
20003
|
+
var AppHomeContextSchema = AppContextSchema.extend({
|
|
20004
|
+
user: external_exports.object({ id: external_exports.string(), name: external_exports.string(), guest: external_exports.boolean() }),
|
|
20005
|
+
space: external_exports.null(),
|
|
20006
|
+
tab: external_exports.null(),
|
|
20007
|
+
/** Which of the app's home pages. */
|
|
20008
|
+
home: external_exports.object({ id: external_exports.string() })
|
|
20009
|
+
});
|
|
20010
|
+
var AppEventKeySchema = external_exports.object({ key: external_exports.string(), eventsUrl: external_exports.string() });
|
|
20011
|
+
var AppEventKeyStatusSchema = external_exports.object({
|
|
20012
|
+
on: external_exports.boolean(),
|
|
20013
|
+
/** Its app declares events (else there's nothing to report). */
|
|
20014
|
+
available: external_exports.boolean(),
|
|
20015
|
+
/** Who allowed it: events report for them. */
|
|
20016
|
+
by: external_exports.object({ id: external_exports.string(), name: external_exports.string() }).nullable(),
|
|
20017
|
+
createdAt: external_exports.string().nullable(),
|
|
20018
|
+
lastUsedAt: external_exports.string().nullable(),
|
|
20019
|
+
/** You may turn it on or off (you manage this space's tabs). */
|
|
20020
|
+
canManage: external_exports.boolean()
|
|
20021
|
+
});
|
|
19879
20022
|
var AppTokenResponseSchema = external_exports.object({
|
|
19880
20023
|
/** A JWT (EdDSA), verified with the deployment's keys at /api/apps/keys. */
|
|
19881
20024
|
token: external_exports.string(),
|
|
19882
20025
|
expiresAt: external_exports.string(),
|
|
19883
|
-
context: AppContextSchema,
|
|
20026
|
+
context: external_exports.union([AppContextSchema, AppHomeContextSchema]),
|
|
19884
20027
|
/** The frame's address and origin (messages go to and come from it only). */
|
|
19885
20028
|
url: external_exports.string(),
|
|
19886
20029
|
origin: external_exports.string()
|
|
@@ -20024,6 +20167,7 @@ var TabSettingsSchema = external_exports.object({
|
|
|
20024
20167
|
});
|
|
20025
20168
|
var AgentManagementSchema = external_exports.enum(["admins", "members"]);
|
|
20026
20169
|
var TaskAssignmentSchema = external_exports.enum(["members", "admins"]);
|
|
20170
|
+
var GroupMentionsSchema = external_exports.enum(["members", "admins"]);
|
|
20027
20171
|
var SpaceSchema = external_exports.object({
|
|
20028
20172
|
id: SpaceIdSchema,
|
|
20029
20173
|
kind: SpaceKindSchema,
|
|
@@ -20043,6 +20187,8 @@ var SpaceSchema = external_exports.object({
|
|
|
20043
20187
|
unpinnedByYou: external_exports.boolean(),
|
|
20044
20188
|
/** In your Starred section. */
|
|
20045
20189
|
starred: external_exports.boolean(),
|
|
20190
|
+
/** How much it notifies you (decision 199): your own choice. */
|
|
20191
|
+
notify: SpaceNotifySchema.default("all"),
|
|
20046
20192
|
/** DMs only: the participants (you included), so each reader can name it (§9.1). */
|
|
20047
20193
|
people: external_exports.array(external_exports.object({ userId: external_exports.string(), name: external_exports.string() })),
|
|
20048
20194
|
/** Your tab row: pinned first, then the rest in your (or the space's) order. */
|
|
@@ -20076,8 +20222,15 @@ var SpaceSchema = external_exports.object({
|
|
|
20076
20222
|
canManage: external_exports.boolean(),
|
|
20077
20223
|
/** Set once archived: hidden from sidebars, restorable or deletable by managers. */
|
|
20078
20224
|
archivedAt: external_exports.iso.datetime().nullable(),
|
|
20079
|
-
createdAt: external_exports.iso.datetime()
|
|
20080
|
-
|
|
20225
|
+
createdAt: external_exports.iso.datetime(),
|
|
20226
|
+
/** Who may notify everyone here with `@space` and `@here` (decision 197). */
|
|
20227
|
+
groupMentions: GroupMentionsSchema.default("members"),
|
|
20228
|
+
/** You may: a member of a normal space as its rule says, General's for workspace admins. */
|
|
20229
|
+
canMentionEveryone: external_exports.boolean().default(false),
|
|
20230
|
+
/** Its icon (normal spaces and General), a picture its managers chose; versioned, so cacheable. */
|
|
20231
|
+
iconUrl: external_exports.string().nullable().default(null)
|
|
20232
|
+
});
|
|
20233
|
+
var MAX_SPACE_ICON_BYTES = 1024 * 1024;
|
|
20081
20234
|
var SpaceListSchema = external_exports.object({
|
|
20082
20235
|
spaces: external_exports.array(SpaceSchema),
|
|
20083
20236
|
canCreate: external_exports.boolean()
|
|
@@ -20092,7 +20245,8 @@ var CreateSpaceRequestSchema = external_exports.object({
|
|
|
20092
20245
|
var UpdateSpaceRequestSchema = external_exports.object({
|
|
20093
20246
|
name: SpaceNameSchema,
|
|
20094
20247
|
description: SpaceDescriptionSchema,
|
|
20095
|
-
visibility: SpaceVisibilitySchema
|
|
20248
|
+
visibility: SpaceVisibilitySchema,
|
|
20249
|
+
groupMentions: GroupMentionsSchema
|
|
20096
20250
|
}).partial().refine((patch) => Object.keys(patch).length > 0, "Nothing to change.");
|
|
20097
20251
|
var ArchivedSpacesResponseSchema = external_exports.object({ spaces: external_exports.array(SpaceSchema) });
|
|
20098
20252
|
var SpaceMemberSchema = external_exports.object({
|
|
@@ -20164,48 +20318,6 @@ var UpdateAgentSettingsRequestSchema = external_exports.object({
|
|
|
20164
20318
|
respondingAgentId: external_exports.string().nullable()
|
|
20165
20319
|
}).partial().refine((patch) => Object.keys(patch).length > 0, "Nothing to change.");
|
|
20166
20320
|
|
|
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
20321
|
// ../types/src/workflows.ts
|
|
20210
20322
|
var MAX_WORKFLOW_STEPS = 30;
|
|
20211
20323
|
var WorkflowStepIdSchema = external_exports.string().regex(/^[a-z0-9]{1,16}$/, "A step id.");
|
|
@@ -20394,6 +20506,7 @@ var FinishPersonStepRequestSchema = external_exports.object({ note: external_exp
|
|
|
20394
20506
|
// ../types/src/triggers.ts
|
|
20395
20507
|
var TaskStatusSchema = external_exports.enum(["todo", "in_progress", "waiting", "done", "cancelled"]);
|
|
20396
20508
|
var MAX_TASK_ASSIGNEES = 10;
|
|
20509
|
+
var INBOX_TRIGGER_KINDS = ["mention", "dm", "thread"];
|
|
20397
20510
|
var TriggerEventSchema = external_exports.discriminatedUnion("kind", [
|
|
20398
20511
|
/** Someone posts in a conversation of the space (optionally with words in it, or by someone). */
|
|
20399
20512
|
external_exports.object({
|
|
@@ -20428,6 +20541,14 @@ var TriggerEventSchema = external_exports.discriminatedUnion("kind", [
|
|
|
20428
20541
|
event: external_exports.string().min(1).max(40),
|
|
20429
20542
|
field: external_exports.string().max(40).nullable().default(null),
|
|
20430
20543
|
value: external_exports.string().trim().max(200).nullable().default(null)
|
|
20544
|
+
}),
|
|
20545
|
+
/**
|
|
20546
|
+
* Something new in your Inbox (decision 198): a personal trigger, only in
|
|
20547
|
+
* your own My Space; what people (not agents) send you, from elsewhere.
|
|
20548
|
+
*/
|
|
20549
|
+
external_exports.object({
|
|
20550
|
+
kind: external_exports.literal("inbox"),
|
|
20551
|
+
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
20552
|
})
|
|
20432
20553
|
]);
|
|
20433
20554
|
var TriggerTaskSchema = external_exports.object({
|
|
@@ -20512,6 +20633,8 @@ var TaskRunStatusSchema = external_exports.enum([
|
|
|
20512
20633
|
var TaskRunErrorSchema = external_exports.enum([
|
|
20513
20634
|
"NOT_CONNECTED",
|
|
20514
20635
|
"NOT_READY",
|
|
20636
|
+
// Every Gateway the deployment runs was busy, each time it tried (decision 121).
|
|
20637
|
+
"BUSY",
|
|
20515
20638
|
"PROVIDER_REFUSED",
|
|
20516
20639
|
"RUN_FAILED",
|
|
20517
20640
|
"BUDGET_TURNS",
|
|
@@ -20654,7 +20777,13 @@ var TaskSeriesSchema = external_exports.object({
|
|
|
20654
20777
|
lastAt: external_exports.string().nullable(),
|
|
20655
20778
|
occurrences: external_exports.number().int(),
|
|
20656
20779
|
createdBy: external_exports.object({ userId: external_exports.string(), name: external_exports.string() }),
|
|
20657
|
-
createdAt: external_exports.string()
|
|
20780
|
+
createdAt: external_exports.string(),
|
|
20781
|
+
/** Made ready-made (decision 198): the unread report. */
|
|
20782
|
+
preset: external_exports.enum(["unread_report"]).nullable().default(null)
|
|
20783
|
+
});
|
|
20784
|
+
var UnreadReportResponseSchema = external_exports.object({ report: TaskSeriesSchema.nullable() });
|
|
20785
|
+
var CreateUnreadReportRequestSchema = external_exports.object({
|
|
20786
|
+
time: ClockTimeSchema.default("08:00")
|
|
20658
20787
|
});
|
|
20659
20788
|
var TaskSeriesResponseSchema = external_exports.object({ series: TaskSeriesSchema });
|
|
20660
20789
|
var CreateTaskSeriesRequestSchema = external_exports.object({
|
|
@@ -20810,7 +20939,9 @@ var ApprovalSchema = external_exports.object({
|
|
|
20810
20939
|
description: external_exports.string(),
|
|
20811
20940
|
input: external_exports.record(external_exports.string(), external_exports.unknown()),
|
|
20812
20941
|
result: external_exports.string().nullable(),
|
|
20813
|
-
error: external_exports.string().nullable()
|
|
20942
|
+
error: external_exports.string().nullable(),
|
|
20943
|
+
/** A connector's tool: whose account it uses (the workspace's, or the asker's own). */
|
|
20944
|
+
account: external_exports.enum(["workspace", "person"]).nullable().default(null)
|
|
20814
20945
|
}).nullable().default(null),
|
|
20815
20946
|
/** An email (M9 slice 4): from, to, subject; its text is `body` (editable before it's sent). */
|
|
20816
20947
|
email: EmailDraftSchema.nullable().default(null),
|
|
@@ -21049,7 +21180,9 @@ var AgentToolSchema = external_exports.enum([
|
|
|
21049
21180
|
// An agent changing its own profile (title, face, instructions…) when asked, approved first.
|
|
21050
21181
|
"update_my_profile",
|
|
21051
21182
|
// Connectors (M12 slice 2): a remote MCP server's tools the workspace chose (reading ones).
|
|
21052
|
-
"use_connector"
|
|
21183
|
+
"use_connector",
|
|
21184
|
+
// Notifications (decision 198): the asker's own Inbox, where only they read the answer.
|
|
21185
|
+
"read_inbox"
|
|
21053
21186
|
]);
|
|
21054
21187
|
var AgentStepSchema = external_exports.object({
|
|
21055
21188
|
tool: AgentToolSchema,
|
|
@@ -21154,7 +21287,9 @@ var ReferenceSchema = external_exports.object({
|
|
|
21154
21287
|
id: external_exports.string(),
|
|
21155
21288
|
kind: external_exports.enum(["general", "personal", "normal", "dm"]),
|
|
21156
21289
|
name: external_exports.string().nullable(),
|
|
21157
|
-
people: external_exports.array(external_exports.object({ userId: external_exports.string(), name: external_exports.string() }))
|
|
21290
|
+
people: external_exports.array(external_exports.object({ userId: external_exports.string(), name: external_exports.string() })),
|
|
21291
|
+
/** Its icon, when it has one (decision 190). */
|
|
21292
|
+
iconUrl: external_exports.string().nullable().default(null)
|
|
21158
21293
|
}).nullable(),
|
|
21159
21294
|
tab: external_exports.object({
|
|
21160
21295
|
id: external_exports.string(),
|
|
@@ -21463,12 +21598,15 @@ var AUDIT_ACTIONS = {
|
|
|
21463
21598
|
"member.invite_link_created",
|
|
21464
21599
|
"member.invitation_revoked",
|
|
21465
21600
|
"member.joined",
|
|
21466
|
-
"member.title_changed"
|
|
21601
|
+
"member.title_changed",
|
|
21602
|
+
"member.role_changed"
|
|
21467
21603
|
],
|
|
21468
21604
|
spaces: [
|
|
21469
21605
|
"space.created",
|
|
21470
21606
|
"space.updated",
|
|
21471
21607
|
"space.deleted",
|
|
21608
|
+
"space.icon_changed",
|
|
21609
|
+
"space.icon_removed",
|
|
21472
21610
|
"space.archived",
|
|
21473
21611
|
"space.unarchived",
|
|
21474
21612
|
"space.converted",
|
|
@@ -21508,6 +21646,9 @@ var AUDIT_ACTIONS = {
|
|
|
21508
21646
|
"agent.avatar_changed",
|
|
21509
21647
|
"agent.host_access_changed",
|
|
21510
21648
|
"agent.memory_changed",
|
|
21649
|
+
"agent.published",
|
|
21650
|
+
"agent.unpublished",
|
|
21651
|
+
"agent.added_from_marketplace",
|
|
21511
21652
|
"approval.requested",
|
|
21512
21653
|
"gateway.connected",
|
|
21513
21654
|
"gateway.disconnected",
|
|
@@ -21536,6 +21677,14 @@ var AUDIT_ACTIONS = {
|
|
|
21536
21677
|
"app.installed",
|
|
21537
21678
|
"app.updated",
|
|
21538
21679
|
"app.removed",
|
|
21680
|
+
"app.added_from_marketplace",
|
|
21681
|
+
"app.published",
|
|
21682
|
+
"app.unpublished",
|
|
21683
|
+
"app.publishing_key_created",
|
|
21684
|
+
"app.publishing_key_revoked",
|
|
21685
|
+
"app.taken_down",
|
|
21686
|
+
"app.event_key_allowed",
|
|
21687
|
+
"app.event_key_turned_off",
|
|
21539
21688
|
"app.action_run",
|
|
21540
21689
|
"connection.added",
|
|
21541
21690
|
"connection.removed",
|
|
@@ -21784,12 +21933,18 @@ var DeploymentSettingsFields = external_exports.object({
|
|
|
21784
21933
|
* for you") keep it awake, never put to sleep (decision 151)? They pay its
|
|
21785
21934
|
* hours where the deployment bills them.
|
|
21786
21935
|
*/
|
|
21787
|
-
allowGatewayKeepAwake: external_exports.boolean()
|
|
21936
|
+
allowGatewayKeepAwake: external_exports.boolean(),
|
|
21937
|
+
/**
|
|
21938
|
+
* Workspaces browse and add from the marketplace (decision 161): Tabs &
|
|
21939
|
+
* Spaces' catalogue, reviewed by us. On unless its admins turn it off.
|
|
21940
|
+
*/
|
|
21941
|
+
marketplace: external_exports.boolean()
|
|
21788
21942
|
});
|
|
21789
21943
|
var DeploymentSettingsSchema = DeploymentSettingsFields.extend({
|
|
21790
21944
|
// Missing in older responses and fixtures: off.
|
|
21791
21945
|
shareFeedback: external_exports.boolean().default(false),
|
|
21792
|
-
allowGatewayKeepAwake: external_exports.boolean().default(false)
|
|
21946
|
+
allowGatewayKeepAwake: external_exports.boolean().default(false),
|
|
21947
|
+
marketplace: external_exports.boolean().default(true)
|
|
21793
21948
|
});
|
|
21794
21949
|
var DeploymentSettingsPatchSchema = DeploymentSettingsFields.partial().strict().refine((patch) => Object.keys(patch).length > 0, "Nothing to update");
|
|
21795
21950
|
var DeploymentInfoSchema = external_exports.object({
|
|
@@ -22073,6 +22228,10 @@ var WorkspaceAccentSchema = external_exports.object({ light: HexColorSchema, dar
|
|
|
22073
22228
|
var WorkspaceRoleSchema = external_exports.enum(["owner", "admin", "member", "guest"]);
|
|
22074
22229
|
var WorkspaceSlugSchema = external_exports.string().regex(/^[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/, "Use lowercase letters, numbers and hyphens");
|
|
22075
22230
|
var WorkspaceNameSchema = external_exports.string().trim().min(1).max(60);
|
|
22231
|
+
var WorkspaceHomePageSchema = external_exports.object({
|
|
22232
|
+
appId: external_exports.string().min(1).max(64),
|
|
22233
|
+
pageId: external_exports.string().min(1).max(40)
|
|
22234
|
+
});
|
|
22076
22235
|
var WorkspaceSchema = external_exports.object({
|
|
22077
22236
|
id: external_exports.string(),
|
|
22078
22237
|
slug: WorkspaceSlugSchema,
|
|
@@ -22086,7 +22245,9 @@ var WorkspaceSchema = external_exports.object({
|
|
|
22086
22245
|
/** Its accent colour for each mode; null: the default ink. */
|
|
22087
22246
|
accent: WorkspaceAccentSchema.nullable().default(null),
|
|
22088
22247
|
/** Its default time zone (M8 slice 1): for schedules when a person's own isn't known. */
|
|
22089
|
-
timeZone: TimeZoneSchema.default(DEFAULT_TIME_ZONE)
|
|
22248
|
+
timeZone: TimeZoneSchema.default(DEFAULT_TIME_ZONE),
|
|
22249
|
+
/** Its Home (decision 177): one of an installed app's home pages; null: ours. */
|
|
22250
|
+
homePage: WorkspaceHomePageSchema.nullable().default(null)
|
|
22090
22251
|
});
|
|
22091
22252
|
var MAX_WORKSPACE_LOGO_BYTES = 2 * 1024 * 1024;
|
|
22092
22253
|
var WorkspaceListSchema = external_exports.object({
|
|
@@ -22194,7 +22355,9 @@ var UpdateWorkspaceRequestSchema = external_exports.object({
|
|
|
22194
22355
|
/** Owners and admins: the accent colour (null: back to the default). */
|
|
22195
22356
|
accent: WorkspaceAccentSchema.nullable(),
|
|
22196
22357
|
/** Owners and admins: the default time zone. */
|
|
22197
|
-
timeZone: TimeZoneSchema
|
|
22358
|
+
timeZone: TimeZoneSchema,
|
|
22359
|
+
/** Owners and admins: Home, from an installed app (null: back to ours). */
|
|
22360
|
+
homePage: WorkspaceHomePageSchema.nullable()
|
|
22198
22361
|
}).partial().strict().refine((patch) => Object.keys(patch).length > 0, "Nothing to update");
|
|
22199
22362
|
var WorkspaceResponseSchema = external_exports.object({ workspace: WorkspaceSchema });
|
|
22200
22363
|
var TRANSLITERATIONS = {
|
|
@@ -22267,10 +22430,13 @@ var WorkspaceMemberSchema = external_exports.object({
|
|
|
22267
22430
|
title: external_exports.string().default(""),
|
|
22268
22431
|
/** Their profile picture's address (null: initials), decision 145. */
|
|
22269
22432
|
picture: external_exports.string().nullable().default(null),
|
|
22270
|
-
joinedAt: external_exports.iso.datetime()
|
|
22433
|
+
joinedAt: external_exports.iso.datetime(),
|
|
22434
|
+
/** Their notifications are paused, or in quiet hours, until then (decision 194); null: not. */
|
|
22435
|
+
quietUntil: external_exports.iso.datetime().nullable().default(null)
|
|
22271
22436
|
});
|
|
22272
22437
|
var TitleSchema = external_exports.string().trim().max(60, "Keep the title under 60 characters.");
|
|
22273
22438
|
var UpdateMemberRequestSchema = external_exports.object({ title: TitleSchema });
|
|
22439
|
+
var ChangeRoleRequestSchema = external_exports.object({ role: WorkspaceRoleSchema }).strict();
|
|
22274
22440
|
var WorkspaceMemberListSchema = external_exports.object({ members: external_exports.array(WorkspaceMemberSchema) });
|
|
22275
22441
|
var MAX_PROFILE_PICTURE_BYTES = 2 * 1024 * 1024;
|
|
22276
22442
|
|
|
@@ -23163,7 +23329,19 @@ var McpConnectorSchema = external_exports.object({
|
|
|
23163
23329
|
addedAt: external_exports.string(),
|
|
23164
23330
|
checkedAt: external_exports.string().nullable()
|
|
23165
23331
|
});
|
|
23166
|
-
var
|
|
23332
|
+
var ConnectorPresetSchema = external_exports.object({
|
|
23333
|
+
id: external_exports.string(),
|
|
23334
|
+
name: external_exports.string(),
|
|
23335
|
+
url: external_exports.string(),
|
|
23336
|
+
auth: external_exports.enum(["oauth", "header"]),
|
|
23337
|
+
headerName: external_exports.string().nullable(),
|
|
23338
|
+
/** How its key is written ("Bearer github_pat_…"). */
|
|
23339
|
+
keyHint: external_exports.string().nullable()
|
|
23340
|
+
});
|
|
23341
|
+
var McpConnectorListSchema = external_exports.object({
|
|
23342
|
+
connectors: external_exports.array(McpConnectorSchema),
|
|
23343
|
+
presets: external_exports.array(ConnectorPresetSchema).default([])
|
|
23344
|
+
});
|
|
23167
23345
|
var HeaderNameSchema = external_exports.string().trim().regex(/^[A-Za-z0-9-]{1,64}$/, "A header's name: letters, digits and dashes.").refine(
|
|
23168
23346
|
(name) => !["host", "content-length", "content-type", "accept", "connection"].includes(
|
|
23169
23347
|
name.toLowerCase()
|
|
@@ -23219,6 +23397,574 @@ var MyConnectorSchema = external_exports.object({
|
|
|
23219
23397
|
var MyConnectorListSchema = external_exports.object({ connectors: external_exports.array(MyConnectorSchema) });
|
|
23220
23398
|
var SetMyConnectorKeyRequestSchema = external_exports.object({ value: external_exports.string().trim().min(1, "Enter your key.").max(4e3) }).strict();
|
|
23221
23399
|
|
|
23400
|
+
// ../types/src/marketplace.ts
|
|
23401
|
+
var LISTING_KINDS = ["agent", "app"];
|
|
23402
|
+
var ListingKindSchema = external_exports.enum(LISTING_KINDS);
|
|
23403
|
+
var LISTING_CATEGORIES = [
|
|
23404
|
+
"productivity",
|
|
23405
|
+
"research",
|
|
23406
|
+
"writing",
|
|
23407
|
+
"support",
|
|
23408
|
+
"sales",
|
|
23409
|
+
"marketing",
|
|
23410
|
+
"engineering",
|
|
23411
|
+
"operations",
|
|
23412
|
+
"finance",
|
|
23413
|
+
"people",
|
|
23414
|
+
"education",
|
|
23415
|
+
"personal"
|
|
23416
|
+
];
|
|
23417
|
+
var ListingCategorySchema = external_exports.enum(LISTING_CATEGORIES);
|
|
23418
|
+
var LISTED_AGENT = "@self";
|
|
23419
|
+
var LISTED_TABBY = "@tabby";
|
|
23420
|
+
var LISTED_ADDER = "@adder";
|
|
23421
|
+
var LISTED_TABS = {
|
|
23422
|
+
conversations: "@conversations",
|
|
23423
|
+
docs: "@docs",
|
|
23424
|
+
tasks: "@tasks"
|
|
23425
|
+
};
|
|
23426
|
+
var listedAppTab = (app) => `@app:${app.id}/${app.tab}`;
|
|
23427
|
+
var AGENT_PLACEHOLDERS = [LISTED_AGENT, LISTED_TABBY];
|
|
23428
|
+
var TAB_PLACEHOLDERS = Object.values(LISTED_TABS);
|
|
23429
|
+
var ASSIGNEE_PLACEHOLDERS = [LISTED_AGENT, LISTED_TABBY, LISTED_ADDER];
|
|
23430
|
+
var ListedAssigneesSchema = external_exports.array(external_exports.enum([LISTED_AGENT, LISTED_TABBY, LISTED_ADDER])).max(3).default([LISTED_AGENT]);
|
|
23431
|
+
var ListedSkillSchema = external_exports.discriminatedUnion("source", [
|
|
23432
|
+
external_exports.object({
|
|
23433
|
+
source: external_exports.literal("builtin"),
|
|
23434
|
+
name: external_exports.string().regex(/^[a-z0-9][a-z0-9-]{0,63}$/, "A skill's name."),
|
|
23435
|
+
displayName: external_exports.string().trim().min(1).max(80)
|
|
23436
|
+
}),
|
|
23437
|
+
external_exports.object({
|
|
23438
|
+
source: external_exports.literal("clawhub"),
|
|
23439
|
+
ref: ClawHubRefSchema,
|
|
23440
|
+
displayName: external_exports.string().trim().min(1).max(80)
|
|
23441
|
+
})
|
|
23442
|
+
]);
|
|
23443
|
+
var ListedConnectorSchema = external_exports.object({
|
|
23444
|
+
name: external_exports.string().trim().min(1).max(40),
|
|
23445
|
+
url: external_exports.url({ protocol: /^https$/, message: "A connector's https:// address." }).max(500),
|
|
23446
|
+
auth: external_exports.enum(["none", "header", "oauth"]),
|
|
23447
|
+
/** With a header: its name (its value is asked for when it's added). */
|
|
23448
|
+
headerName: external_exports.string().regex(/^[A-Za-z0-9-]{1,64}$/).nullable().default(null),
|
|
23449
|
+
identity: ConnectorIdentitySchema.default("workspace"),
|
|
23450
|
+
/** OAuth: what its sign-in asks for (null: what its server suggests, or our default for it). */
|
|
23451
|
+
scope: external_exports.string().trim().max(500).nullable().default(null),
|
|
23452
|
+
/** What it's for, for whoever adds it. */
|
|
23453
|
+
description: external_exports.string().trim().max(300).default(""),
|
|
23454
|
+
/** Its tools the agent may use, by name (the rest stay off). */
|
|
23455
|
+
tools: external_exports.array(external_exports.string().max(200)).max(500).default([])
|
|
23456
|
+
});
|
|
23457
|
+
var ListedAppSchema = external_exports.object({
|
|
23458
|
+
/** Its manifest's `id`. */
|
|
23459
|
+
id: external_exports.string().min(3).max(64),
|
|
23460
|
+
name: external_exports.string().trim().min(1).max(40),
|
|
23461
|
+
manifestUrl: external_exports.url({ protocol: /^https?$/, message: "The manifest's address." }).max(500),
|
|
23462
|
+
/** Which of its tabs goes in the space: `listedAppTab()` names it. */
|
|
23463
|
+
tab: external_exports.string().min(1).max(40),
|
|
23464
|
+
/** What it's for, for whoever adds it. */
|
|
23465
|
+
description: external_exports.string().trim().max(300).default("")
|
|
23466
|
+
});
|
|
23467
|
+
var ListedWorkflowSchema = external_exports.object({
|
|
23468
|
+
key: WorkflowStepIdSchema,
|
|
23469
|
+
name: external_exports.string().trim().min(1).max(120),
|
|
23470
|
+
description: external_exports.string().max(2e3).default(""),
|
|
23471
|
+
steps: external_exports.array(WorkflowStepSchema).min(1).max(30)
|
|
23472
|
+
});
|
|
23473
|
+
var ListedSeriesSchema = external_exports.object({
|
|
23474
|
+
title: external_exports.string().trim().min(1).max(200),
|
|
23475
|
+
description: external_exports.string().max(4e3).default(""),
|
|
23476
|
+
assignees: ListedAssigneesSchema,
|
|
23477
|
+
recurrence: RecurrenceSchema,
|
|
23478
|
+
time: ClockTimeSchema,
|
|
23479
|
+
/** Its results are also posted in a conversation (`@conversations`). */
|
|
23480
|
+
postTo: external_exports.literal(LISTED_TABS.conversations).nullable().default(null),
|
|
23481
|
+
approveResults: external_exports.boolean().default(false),
|
|
23482
|
+
workflowKey: WorkflowStepIdSchema.nullable().default(null)
|
|
23483
|
+
});
|
|
23484
|
+
var ListedTriggerSchema = external_exports.object({
|
|
23485
|
+
name: external_exports.string().trim().min(1).max(120),
|
|
23486
|
+
event: TriggerEventSchema,
|
|
23487
|
+
task: external_exports.object({
|
|
23488
|
+
title: external_exports.string().trim().min(1).max(200),
|
|
23489
|
+
description: external_exports.string().max(4e3).default(""),
|
|
23490
|
+
assignees: ListedAssigneesSchema,
|
|
23491
|
+
workflowKey: WorkflowStepIdSchema.nullable().default(null)
|
|
23492
|
+
})
|
|
23493
|
+
});
|
|
23494
|
+
var AgentListingContentSchema = external_exports.object({
|
|
23495
|
+
agent: external_exports.object({
|
|
23496
|
+
name: external_exports.string().trim().min(1).max(40),
|
|
23497
|
+
title: external_exports.string().trim().max(60).default(""),
|
|
23498
|
+
// An agent's own limits, so whatever's listed can be added.
|
|
23499
|
+
description: external_exports.string().trim().min(1, "Say what it's for.").max(120),
|
|
23500
|
+
instructions: external_exports.string().max(MAX_AGENT_INSTRUCTIONS).default(""),
|
|
23501
|
+
/** One of the drawn faces; or "custom", with the listing's picture. */
|
|
23502
|
+
avatar: external_exports.enum([...AGENT_AVATARS, "custom"])
|
|
23503
|
+
}),
|
|
23504
|
+
skills: external_exports.array(ListedSkillSchema).max(30).default([]),
|
|
23505
|
+
connectors: external_exports.array(ListedConnectorSchema).max(10).default([]),
|
|
23506
|
+
apps: external_exports.array(ListedAppSchema).max(3).default([]),
|
|
23507
|
+
workflows: external_exports.array(ListedWorkflowSchema).max(10).default([]),
|
|
23508
|
+
series: external_exports.array(ListedSeriesSchema).max(10).default([]),
|
|
23509
|
+
triggers: external_exports.array(ListedTriggerSchema).max(10).default([])
|
|
23510
|
+
}).superRefine((content, ctx) => {
|
|
23511
|
+
for (const problem of portabilityProblems(content))
|
|
23512
|
+
ctx.addIssue({ code: "custom", ...problem });
|
|
23513
|
+
});
|
|
23514
|
+
function portabilityProblems(content) {
|
|
23515
|
+
const problems = [];
|
|
23516
|
+
const keys = new Set(content.workflows.map((w) => w.key));
|
|
23517
|
+
const appTabs = new Set((content.apps ?? []).map(listedAppTab));
|
|
23518
|
+
const agent = (id, path) => {
|
|
23519
|
+
if (!AGENT_PLACEHOLDERS.includes(id)) {
|
|
23520
|
+
problems.push({ message: "It can only use this agent or Tabby.", path });
|
|
23521
|
+
}
|
|
23522
|
+
};
|
|
23523
|
+
const tab = (id, path) => {
|
|
23524
|
+
if (!TAB_PLACEHOLDERS.includes(id) && !appTabs.has(id)) {
|
|
23525
|
+
problems.push({ message: "It can only use a conversation, Docs or Tasks tab.", path });
|
|
23526
|
+
}
|
|
23527
|
+
};
|
|
23528
|
+
const person = (id, path) => {
|
|
23529
|
+
if (!ASSIGNEE_PLACEHOLDERS.includes(id)) {
|
|
23530
|
+
problems.push({ message: "It can only be for this agent, Tabby or whoever adds it.", path });
|
|
23531
|
+
}
|
|
23532
|
+
};
|
|
23533
|
+
const workflow = (key, path) => {
|
|
23534
|
+
if (key !== null && !keys.has(key)) {
|
|
23535
|
+
problems.push({ message: "It names a workflow the listing doesn't have.", path });
|
|
23536
|
+
}
|
|
23537
|
+
};
|
|
23538
|
+
content.workflows.forEach((w, i) => {
|
|
23539
|
+
if (content.workflows.findIndex((other) => other.key === w.key) !== i) {
|
|
23540
|
+
problems.push({ message: "Workflows' keys must differ.", path: ["workflows", i, "key"] });
|
|
23541
|
+
}
|
|
23542
|
+
for (const step of allSteps(w.steps)) {
|
|
23543
|
+
const at = ["workflows", i, "steps", step.id];
|
|
23544
|
+
switch (step.kind) {
|
|
23545
|
+
case "agent":
|
|
23546
|
+
agent(step.agentId, at);
|
|
23547
|
+
break;
|
|
23548
|
+
case "post":
|
|
23549
|
+
agent(step.agentId, at);
|
|
23550
|
+
tab(step.tabId, at);
|
|
23551
|
+
break;
|
|
23552
|
+
case "create_task":
|
|
23553
|
+
tab(step.tabId, at);
|
|
23554
|
+
step.assigneeIds.forEach((id) => person(id, at));
|
|
23555
|
+
break;
|
|
23556
|
+
case "person":
|
|
23557
|
+
person(step.assigneeId, at);
|
|
23558
|
+
break;
|
|
23559
|
+
case "if":
|
|
23560
|
+
if (step.condition.op === "agent") agent(step.condition.agentId, at);
|
|
23561
|
+
break;
|
|
23562
|
+
case "app_action":
|
|
23563
|
+
if (!appTabs.has(step.tabId)) {
|
|
23564
|
+
problems.push({ message: "Its actions need an app the listing brings.", path: at });
|
|
23565
|
+
}
|
|
23566
|
+
break;
|
|
23567
|
+
}
|
|
23568
|
+
}
|
|
23569
|
+
});
|
|
23570
|
+
content.series.forEach((s, i) => workflow(s.workflowKey, ["series", i, "workflowKey"]));
|
|
23571
|
+
content.triggers.forEach((t, i) => {
|
|
23572
|
+
const at = ["triggers", i, "event"];
|
|
23573
|
+
const event = t.event;
|
|
23574
|
+
if (event.kind === "app") {
|
|
23575
|
+
if (!appTabs.has(event.tabId)) {
|
|
23576
|
+
problems.push({ message: "Its events need an app the listing brings.", path: at });
|
|
23577
|
+
}
|
|
23578
|
+
} else if (event.kind === "inbox") {
|
|
23579
|
+
problems.push({ message: "A personal trigger (someone's Inbox) can't be listed.", path: at });
|
|
23580
|
+
} else if (event.kind !== "webhook") tab(event.tabId, at);
|
|
23581
|
+
if (event.kind === "message" && event.fromId !== null) person(event.fromId, at);
|
|
23582
|
+
workflow(t.task.workflowKey, ["triggers", i, "task", "workflowKey"]);
|
|
23583
|
+
});
|
|
23584
|
+
return problems;
|
|
23585
|
+
}
|
|
23586
|
+
var ListingPublisherSchema = external_exports.object({
|
|
23587
|
+
/** The workspace that publishes it, or Tabs & Spaces. */
|
|
23588
|
+
name: external_exports.string(),
|
|
23589
|
+
/** Made by Tabs & Spaces (decision 167): kept in our repository, listed as it changes. */
|
|
23590
|
+
official: external_exports.boolean().default(false)
|
|
23591
|
+
});
|
|
23592
|
+
var ListingNeedsSchema = external_exports.object({
|
|
23593
|
+
skills: external_exports.number().int().nonnegative(),
|
|
23594
|
+
connectors: external_exports.number().int().nonnegative(),
|
|
23595
|
+
workflows: external_exports.number().int().nonnegative(),
|
|
23596
|
+
recurring: external_exports.number().int().nonnegative(),
|
|
23597
|
+
triggers: external_exports.number().int().nonnegative(),
|
|
23598
|
+
tabs: external_exports.number().int().nonnegative().default(0),
|
|
23599
|
+
/** An agent's apps (decision 178). */
|
|
23600
|
+
apps: external_exports.number().int().nonnegative().default(0),
|
|
23601
|
+
actions: external_exports.number().int().nonnegative().default(0),
|
|
23602
|
+
events: external_exports.number().int().nonnegative().default(0)
|
|
23603
|
+
});
|
|
23604
|
+
var ListingSummarySchema = external_exports.object({
|
|
23605
|
+
id: external_exports.string(),
|
|
23606
|
+
slug: external_exports.string(),
|
|
23607
|
+
kind: ListingKindSchema,
|
|
23608
|
+
name: external_exports.string(),
|
|
23609
|
+
/** Its job title (agents), or a tagline. */
|
|
23610
|
+
title: external_exports.string(),
|
|
23611
|
+
description: external_exports.string(),
|
|
23612
|
+
categories: external_exports.array(ListingCategorySchema),
|
|
23613
|
+
avatar: external_exports.string(),
|
|
23614
|
+
/** Its own picture's address, if it has one. */
|
|
23615
|
+
pictureUrl: external_exports.string().nullable(),
|
|
23616
|
+
publisher: ListingPublisherSchema,
|
|
23617
|
+
version: external_exports.number().int().positive(),
|
|
23618
|
+
installs: external_exports.number().int().nonnegative(),
|
|
23619
|
+
needs: ListingNeedsSchema,
|
|
23620
|
+
publishedAt: external_exports.iso.datetime(),
|
|
23621
|
+
updatedAt: external_exports.iso.datetime()
|
|
23622
|
+
});
|
|
23623
|
+
var ListingSearchSchema = external_exports.object({
|
|
23624
|
+
listings: external_exports.array(ListingSummarySchema),
|
|
23625
|
+
total: external_exports.number().int().nonnegative()
|
|
23626
|
+
});
|
|
23627
|
+
var ListingImageViewSchema = external_exports.object({
|
|
23628
|
+
id: external_exports.string(),
|
|
23629
|
+
url: external_exports.string(),
|
|
23630
|
+
caption: external_exports.string()
|
|
23631
|
+
});
|
|
23632
|
+
var ListingPage = ListingSummarySchema.extend({
|
|
23633
|
+
/** A longer account of it, in Markdown. */
|
|
23634
|
+
about: external_exports.string(),
|
|
23635
|
+
/** What changed in this version. */
|
|
23636
|
+
changes: external_exports.string(),
|
|
23637
|
+
/** Screenshots and pictures of it at work (decision 202), in order. */
|
|
23638
|
+
images: external_exports.array(ListingImageViewSchema).default([])
|
|
23639
|
+
});
|
|
23640
|
+
var AgentListingSchema = ListingPage.extend({
|
|
23641
|
+
kind: external_exports.literal("agent"),
|
|
23642
|
+
content: AgentListingContentSchema
|
|
23643
|
+
});
|
|
23644
|
+
var AppListingSchema = ListingPage.extend({
|
|
23645
|
+
kind: external_exports.literal("app"),
|
|
23646
|
+
content: external_exports.lazy(() => AppListingContentSchema)
|
|
23647
|
+
});
|
|
23648
|
+
var ListingSchema = external_exports.discriminatedUnion("kind", [AgentListingSchema, AppListingSchema]);
|
|
23649
|
+
var ListingResponseSchema = external_exports.object({ listing: ListingSchema });
|
|
23650
|
+
var ListingPictureSchema = external_exports.object({
|
|
23651
|
+
type: external_exports.enum(["image/png", "image/jpeg", "image/webp"]),
|
|
23652
|
+
base64: external_exports.string().max(35e4, "The picture is too big (256 kB at most).").regex(/^[A-Za-z0-9+/]+=*$/)
|
|
23653
|
+
});
|
|
23654
|
+
var MAX_LISTING_IMAGES = 6;
|
|
23655
|
+
var MAX_LISTING_IMAGE_BYTES = 1048576;
|
|
23656
|
+
var ListingImageIdSchema = external_exports.string().regex(/^[0-9a-f]{64}$/, "An image's id.");
|
|
23657
|
+
var ImageCaption = external_exports.string().trim().max(140, "A caption is 140 characters at most.").default("");
|
|
23658
|
+
var ListingImageSchema = external_exports.union([
|
|
23659
|
+
external_exports.object({
|
|
23660
|
+
type: external_exports.enum(["image/png", "image/jpeg", "image/webp"]),
|
|
23661
|
+
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+/]+=*$/),
|
|
23662
|
+
caption: ImageCaption
|
|
23663
|
+
}).strict(),
|
|
23664
|
+
external_exports.object({ id: ListingImageIdSchema, caption: ImageCaption }).strict()
|
|
23665
|
+
]);
|
|
23666
|
+
var ListingImages = external_exports.array(ListingImageSchema).max(MAX_LISTING_IMAGES, `${MAX_LISTING_IMAGES} images at most.`).default([]);
|
|
23667
|
+
var PrivacyUrlSchema = external_exports.url({ protocol: /^https$/, message: "The privacy policy's address, starting https://." }).max(500);
|
|
23668
|
+
var AppListingContentSchema = external_exports.object({
|
|
23669
|
+
app: external_exports.object({
|
|
23670
|
+
/** Its manifest's `id`. */
|
|
23671
|
+
id: external_exports.string().min(3).max(64),
|
|
23672
|
+
name: external_exports.string().trim().min(1).max(40),
|
|
23673
|
+
/** A line about it. */
|
|
23674
|
+
title: external_exports.string().trim().max(60).default(""),
|
|
23675
|
+
description: external_exports.string().trim().min(1).max(300),
|
|
23676
|
+
// https, as installing requires (http only where a deployment allows private networks).
|
|
23677
|
+
manifestUrl: external_exports.url({ protocol: /^https?$/, message: "The manifest's address." }).max(500),
|
|
23678
|
+
/**
|
|
23679
|
+
* The manifest we reviewed (decision 203): installing from the listing refuses
|
|
23680
|
+
* another. Ours have none (we serve them, and change them as we deploy).
|
|
23681
|
+
*/
|
|
23682
|
+
manifestHash: ManifestHashSchema.optional(),
|
|
23683
|
+
/**
|
|
23684
|
+
* Its developer's privacy policy (decision 205): what happens to the data
|
|
23685
|
+
* the app is sent. Every workspace's app has one; ours are on our site.
|
|
23686
|
+
*/
|
|
23687
|
+
privacyUrl: PrivacyUrlSchema.optional()
|
|
23688
|
+
}),
|
|
23689
|
+
tabs: external_exports.array(
|
|
23690
|
+
external_exports.object({
|
|
23691
|
+
id: external_exports.string().max(40),
|
|
23692
|
+
name: external_exports.string().max(40),
|
|
23693
|
+
description: external_exports.string().max(200).default("")
|
|
23694
|
+
})
|
|
23695
|
+
).min(1).max(10),
|
|
23696
|
+
actions: external_exports.array(
|
|
23697
|
+
external_exports.object({
|
|
23698
|
+
name: external_exports.string().max(40),
|
|
23699
|
+
description: external_exports.string().max(300),
|
|
23700
|
+
changes: external_exports.boolean()
|
|
23701
|
+
})
|
|
23702
|
+
).max(20).default([]),
|
|
23703
|
+
events: external_exports.array(external_exports.object({ name: external_exports.string().max(40), description: external_exports.string().max(300) })).max(20).default([])
|
|
23704
|
+
});
|
|
23705
|
+
var DraftCommon = {
|
|
23706
|
+
categories: external_exports.array(ListingCategorySchema).min(1, "Choose a category.").max(3),
|
|
23707
|
+
about: external_exports.string().max(1e4).default(""),
|
|
23708
|
+
changes: external_exports.string().max(2e3).default(""),
|
|
23709
|
+
picture: ListingPictureSchema.nullable().default(null),
|
|
23710
|
+
images: ListingImages
|
|
23711
|
+
};
|
|
23712
|
+
var AgentListingDraftSchema = external_exports.object({
|
|
23713
|
+
kind: external_exports.literal("agent"),
|
|
23714
|
+
...DraftCommon,
|
|
23715
|
+
content: AgentListingContentSchema
|
|
23716
|
+
});
|
|
23717
|
+
var ListingDraftSchema = external_exports.discriminatedUnion("kind", [
|
|
23718
|
+
AgentListingDraftSchema,
|
|
23719
|
+
external_exports.object({ kind: external_exports.literal("app"), ...DraftCommon, content: AppListingContentSchema })
|
|
23720
|
+
]);
|
|
23721
|
+
var PublishedFrom = {
|
|
23722
|
+
/** Its listing, for a new version (one this workspace published). */
|
|
23723
|
+
listingId: external_exports.string().max(64).nullable().default(null),
|
|
23724
|
+
workspace: external_exports.object({ id: external_exports.string().min(1).max(64), name: external_exports.string().trim().min(1).max(120) }),
|
|
23725
|
+
/** Who sent it (we write to them about the review; never shown). */
|
|
23726
|
+
publishedBy: external_exports.object({ name: external_exports.string().trim().min(1).max(120), email: external_exports.email() }),
|
|
23727
|
+
/**
|
|
23728
|
+
* The publisher terms' version its sender accepted just now (decision 205;
|
|
23729
|
+
* null: the workspace accepted these terms before).
|
|
23730
|
+
*/
|
|
23731
|
+
acceptedTerms: external_exports.string().max(20).nullable().default(null)
|
|
23732
|
+
};
|
|
23733
|
+
var ControlPublishAppSchema = external_exports.object({
|
|
23734
|
+
kind: external_exports.literal("app"),
|
|
23735
|
+
...DraftCommon,
|
|
23736
|
+
manifestUrl: external_exports.url({ protocol: /^https?$/, message: "The manifest's address." }).max(500),
|
|
23737
|
+
manifestHash: ManifestHashSchema,
|
|
23738
|
+
privacyUrl: PrivacyUrlSchema,
|
|
23739
|
+
...PublishedFrom
|
|
23740
|
+
});
|
|
23741
|
+
var ControlPublishListingSchema = external_exports.discriminatedUnion("kind", [
|
|
23742
|
+
AgentListingDraftSchema.extend(PublishedFrom),
|
|
23743
|
+
ControlPublishAppSchema
|
|
23744
|
+
]);
|
|
23745
|
+
var ListingStatusSchema = external_exports.enum(["pending", "approved", "rejected", "removed"]);
|
|
23746
|
+
var PublishedListingSchema = external_exports.object({
|
|
23747
|
+
id: external_exports.string(),
|
|
23748
|
+
slug: external_exports.string(),
|
|
23749
|
+
kind: ListingKindSchema,
|
|
23750
|
+
name: external_exports.string(),
|
|
23751
|
+
/** The version everyone sees (null: none approved yet). */
|
|
23752
|
+
liveVersion: external_exports.number().int().positive().nullable(),
|
|
23753
|
+
/** The newest version and its review. */
|
|
23754
|
+
latest: external_exports.object({
|
|
23755
|
+
version: external_exports.number().int().positive(),
|
|
23756
|
+
status: external_exports.enum(["pending", "approved", "rejected"]),
|
|
23757
|
+
/** Why we didn't list it. */
|
|
23758
|
+
reason: external_exports.string().nullable(),
|
|
23759
|
+
submittedAt: external_exports.iso.datetime()
|
|
23760
|
+
}),
|
|
23761
|
+
/** Taken down by us (with why), or by the workspace. */
|
|
23762
|
+
removed: external_exports.object({ reason: external_exports.string(), at: external_exports.iso.datetime() }).nullable(),
|
|
23763
|
+
installs: external_exports.number().int().nonnegative()
|
|
23764
|
+
});
|
|
23765
|
+
var PublishedListingsSchema = external_exports.object({
|
|
23766
|
+
listings: external_exports.array(PublishedListingSchema),
|
|
23767
|
+
/**
|
|
23768
|
+
* The workspace's token (decision 203): an app's manifest names it in
|
|
23769
|
+
* `marketplace.verify` to say this workspace may publish it.
|
|
23770
|
+
*/
|
|
23771
|
+
publisherToken: external_exports.string().nullable().default(null),
|
|
23772
|
+
/** The publisher terms (decision 205), and the version this workspace accepted. */
|
|
23773
|
+
terms: external_exports.object({ version: external_exports.string(), url: external_exports.string(), accepted: external_exports.string().nullable() }).nullable().default(null)
|
|
23774
|
+
});
|
|
23775
|
+
var PublisherTermsSchema = external_exports.object({
|
|
23776
|
+
version: external_exports.string(),
|
|
23777
|
+
url: external_exports.string(),
|
|
23778
|
+
/** This workspace accepted this version. */
|
|
23779
|
+
accepted: external_exports.boolean()
|
|
23780
|
+
});
|
|
23781
|
+
var ListingStatusRequestSchema = external_exports.object({
|
|
23782
|
+
ids: external_exports.array(external_exports.string().min(1).max(64)).min(1).max(100)
|
|
23783
|
+
});
|
|
23784
|
+
var ListingStatusesSchema = external_exports.object({
|
|
23785
|
+
listings: external_exports.array(
|
|
23786
|
+
external_exports.object({
|
|
23787
|
+
id: external_exports.string(),
|
|
23788
|
+
/** Live, for anyone to add. */
|
|
23789
|
+
listed: external_exports.boolean(),
|
|
23790
|
+
/** Taken down by Tabs & Spaces, and why (its workspace taking it out isn't this). */
|
|
23791
|
+
takenDown: external_exports.object({ reason: external_exports.string(), at: external_exports.iso.datetime() }).nullable()
|
|
23792
|
+
})
|
|
23793
|
+
)
|
|
23794
|
+
});
|
|
23795
|
+
var PublishingItemSchema = external_exports.object({
|
|
23796
|
+
id: external_exports.string(),
|
|
23797
|
+
name: external_exports.string(),
|
|
23798
|
+
/** A line about it ("ClawHub: @acme/notes", "Every Friday at 16:00"). */
|
|
23799
|
+
detail: external_exports.string(),
|
|
23800
|
+
/** Ticked to start with. */
|
|
23801
|
+
suggested: external_exports.boolean(),
|
|
23802
|
+
/** Why it can't be listed (empty: it can). */
|
|
23803
|
+
problems: external_exports.array(external_exports.string())
|
|
23804
|
+
});
|
|
23805
|
+
var AgentPublishingSchema = external_exports.object({
|
|
23806
|
+
/** This deployment is registered with Tabs & Spaces (publishing needs it). */
|
|
23807
|
+
registered: external_exports.boolean(),
|
|
23808
|
+
/** The agent's listing, if it was published. */
|
|
23809
|
+
listing: PublishedListingSchema.nullable(),
|
|
23810
|
+
/** Its live version's words, to start a new version from. */
|
|
23811
|
+
previous: external_exports.object({
|
|
23812
|
+
categories: external_exports.array(ListingCategorySchema),
|
|
23813
|
+
about: external_exports.string(),
|
|
23814
|
+
/** Its images, kept by id unless they're taken out (decision 202). */
|
|
23815
|
+
images: external_exports.array(ListingImageViewSchema).default([])
|
|
23816
|
+
}).nullable(),
|
|
23817
|
+
/** Where the marketplace shows a listing (its slug follows). */
|
|
23818
|
+
listingBase: external_exports.string(),
|
|
23819
|
+
/** The publisher terms (null: not registered, or unknown). */
|
|
23820
|
+
terms: PublisherTermsSchema.nullable().default(null),
|
|
23821
|
+
skills: external_exports.array(PublishingItemSchema),
|
|
23822
|
+
connectors: external_exports.array(PublishingItemSchema),
|
|
23823
|
+
/** Spaces where it has workflows, recurring tasks or triggers. */
|
|
23824
|
+
spaces: external_exports.array(external_exports.object({ id: external_exports.string(), name: external_exports.string() })),
|
|
23825
|
+
/** The space whose work is shown below (null: none). */
|
|
23826
|
+
spaceId: external_exports.string().nullable(),
|
|
23827
|
+
workflows: external_exports.array(PublishingItemSchema),
|
|
23828
|
+
series: external_exports.array(PublishingItemSchema),
|
|
23829
|
+
triggers: external_exports.array(PublishingItemSchema)
|
|
23830
|
+
});
|
|
23831
|
+
var PublishAgentRequestSchema = external_exports.object({
|
|
23832
|
+
categories: external_exports.array(ListingCategorySchema).min(1, "Choose a category.").max(3),
|
|
23833
|
+
about: external_exports.string().max(1e4).default(""),
|
|
23834
|
+
changes: external_exports.string().max(2e3).default(""),
|
|
23835
|
+
spaceId: external_exports.string().max(64).nullable().default(null),
|
|
23836
|
+
skills: external_exports.array(external_exports.string().max(200)).max(30).default([]),
|
|
23837
|
+
connectors: external_exports.array(external_exports.string().max(64)).max(10).default([]),
|
|
23838
|
+
workflows: external_exports.array(external_exports.string().max(64)).max(10).default([]),
|
|
23839
|
+
series: external_exports.array(external_exports.string().max(64)).max(10).default([]),
|
|
23840
|
+
triggers: external_exports.array(external_exports.string().max(64)).max(10).default([]),
|
|
23841
|
+
images: ListingImages,
|
|
23842
|
+
/** The publisher terms' version accepted now (decision 205; left out: accepted before). */
|
|
23843
|
+
acceptTerms: external_exports.string().max(20).optional()
|
|
23844
|
+
}).strict();
|
|
23845
|
+
var PublishedListingResponseSchema = external_exports.object({ listing: PublishedListingSchema });
|
|
23846
|
+
var AppPublishingSchema = external_exports.object({
|
|
23847
|
+
/** This deployment is registered with Tabs & Spaces (publishing needs it). */
|
|
23848
|
+
registered: external_exports.boolean(),
|
|
23849
|
+
/** The app's listing, if this workspace published it. */
|
|
23850
|
+
listing: PublishedListingSchema.nullable(),
|
|
23851
|
+
/** Its live version's words and images, to start a new version from. */
|
|
23852
|
+
previous: external_exports.object({
|
|
23853
|
+
categories: external_exports.array(ListingCategorySchema),
|
|
23854
|
+
about: external_exports.string(),
|
|
23855
|
+
images: external_exports.array(ListingImageViewSchema).default([]),
|
|
23856
|
+
privacyUrl: external_exports.string().nullable().default(null)
|
|
23857
|
+
}).nullable(),
|
|
23858
|
+
listingBase: external_exports.string(),
|
|
23859
|
+
terms: PublisherTermsSchema.nullable().default(null),
|
|
23860
|
+
/** What its manifest says to name this workspace (null: not registered, or unknown). */
|
|
23861
|
+
publisherToken: external_exports.string().nullable(),
|
|
23862
|
+
/** The installed manifest names it. */
|
|
23863
|
+
verified: external_exports.boolean(),
|
|
23864
|
+
/** Why it can't be sent yet, as the installed manifest stands. */
|
|
23865
|
+
problems: external_exports.array(external_exports.string())
|
|
23866
|
+
});
|
|
23867
|
+
var PublishAppRequestSchema = external_exports.object({
|
|
23868
|
+
categories: external_exports.array(ListingCategorySchema).min(1, "Choose a category.").max(3),
|
|
23869
|
+
about: external_exports.string().max(1e4).default(""),
|
|
23870
|
+
changes: external_exports.string().max(2e3).default(""),
|
|
23871
|
+
images: ListingImages,
|
|
23872
|
+
privacyUrl: PrivacyUrlSchema,
|
|
23873
|
+
acceptTerms: external_exports.string().max(20).optional()
|
|
23874
|
+
}).strict();
|
|
23875
|
+
var PublishingKeySchema = external_exports.object({
|
|
23876
|
+
id: external_exports.string(),
|
|
23877
|
+
/** Its first characters, to tell keys apart (the rest is never shown again). */
|
|
23878
|
+
prefix: external_exports.string(),
|
|
23879
|
+
createdBy: external_exports.object({ name: external_exports.string() }),
|
|
23880
|
+
createdAt: external_exports.iso.datetime(),
|
|
23881
|
+
lastUsedAt: external_exports.iso.datetime().nullable()
|
|
23882
|
+
});
|
|
23883
|
+
var PublishingKeysSchema = external_exports.object({ keys: external_exports.array(PublishingKeySchema) });
|
|
23884
|
+
var CreatedPublishingKeySchema = external_exports.object({
|
|
23885
|
+
key: PublishingKeySchema,
|
|
23886
|
+
/** Shown once: `tands publish` sends it (TANDS_PUBLISH_KEY). */
|
|
23887
|
+
secret: external_exports.string()
|
|
23888
|
+
});
|
|
23889
|
+
var KeyPublishRequestSchema = external_exports.object({ changes: external_exports.string().max(2e3).default("") }).strict();
|
|
23890
|
+
var KeyPublishResultSchema = external_exports.object({
|
|
23891
|
+
app: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
|
|
23892
|
+
listing: PublishedListingSchema,
|
|
23893
|
+
/** The installed manifest changed (checked for updates first). */
|
|
23894
|
+
updated: external_exports.boolean()
|
|
23895
|
+
});
|
|
23896
|
+
var MarketplaceBrowseSchema = external_exports.object({
|
|
23897
|
+
/** Off: its deployment's admins turned the marketplace off. */
|
|
23898
|
+
enabled: external_exports.boolean(),
|
|
23899
|
+
listings: external_exports.array(ListingSummarySchema),
|
|
23900
|
+
total: external_exports.number().int().nonnegative(),
|
|
23901
|
+
/** Listings this workspace has added: listing id → its agent. */
|
|
23902
|
+
added: external_exports.record(external_exports.string(), external_exports.string()),
|
|
23903
|
+
/** Workspace owners and admins add them (skills and connectors are theirs to set up). */
|
|
23904
|
+
canAdd: external_exports.boolean()
|
|
23905
|
+
});
|
|
23906
|
+
var MarketplaceListingSchema = external_exports.object({
|
|
23907
|
+
listing: ListingSchema,
|
|
23908
|
+
/** The agent it was added as, if it was. */
|
|
23909
|
+
addedAs: external_exports.string().nullable(),
|
|
23910
|
+
/** What the workspace has already (it's shared, not added twice). */
|
|
23911
|
+
has: external_exports.object({ skills: external_exports.array(external_exports.string()), connectors: external_exports.array(external_exports.string()) }),
|
|
23912
|
+
/**
|
|
23913
|
+
* How each of its connectors signs in here (by its place in the listing):
|
|
23914
|
+
* a known server (GitHub) uses OAuth where this deployment registered its
|
|
23915
|
+
* app, else each person's own key in `headerName` (decision 167).
|
|
23916
|
+
*/
|
|
23917
|
+
connectorAuth: external_exports.array(
|
|
23918
|
+
external_exports.object({
|
|
23919
|
+
auth: external_exports.enum(["none", "header", "oauth"]),
|
|
23920
|
+
headerName: external_exports.string().nullable(),
|
|
23921
|
+
keyHint: external_exports.string().nullable()
|
|
23922
|
+
})
|
|
23923
|
+
).default([]),
|
|
23924
|
+
canAdd: external_exports.boolean()
|
|
23925
|
+
});
|
|
23926
|
+
var AddListingRequestSchema = external_exports.object({
|
|
23927
|
+
/** Where its work goes (needed when it has some). */
|
|
23928
|
+
spaceId: external_exports.string().max(64).nullable().default(null),
|
|
23929
|
+
/** Its recurring tasks' time zone: the adder's. */
|
|
23930
|
+
timeZone: external_exports.string().min(1).max(64),
|
|
23931
|
+
/** Keys for its connectors that sign in with one, by the connector's place in the listing. */
|
|
23932
|
+
keys: external_exports.record(external_exports.string().regex(/^\d{1,2}$/), external_exports.string().trim().min(1).max(4e3)).default({})
|
|
23933
|
+
}).strict();
|
|
23934
|
+
var AddedStepSchema = external_exports.object({
|
|
23935
|
+
what: external_exports.enum([
|
|
23936
|
+
"agent",
|
|
23937
|
+
"picture",
|
|
23938
|
+
"skill",
|
|
23939
|
+
"connector",
|
|
23940
|
+
"space",
|
|
23941
|
+
"workflow",
|
|
23942
|
+
"series",
|
|
23943
|
+
"trigger",
|
|
23944
|
+
"app",
|
|
23945
|
+
"tab"
|
|
23946
|
+
]),
|
|
23947
|
+
name: external_exports.string(),
|
|
23948
|
+
ok: external_exports.boolean(),
|
|
23949
|
+
/** Why it didn't work (it can be done by hand). */
|
|
23950
|
+
message: external_exports.string().nullable()
|
|
23951
|
+
});
|
|
23952
|
+
var AddListingResultSchema = external_exports.object({
|
|
23953
|
+
/** The agent it was added as. */
|
|
23954
|
+
agent: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
|
|
23955
|
+
steps: external_exports.array(AddedStepSchema),
|
|
23956
|
+
/** Connectors waiting for an OAuth sign-in. */
|
|
23957
|
+
signIns: external_exports.array(external_exports.object({ connectorId: external_exports.string(), name: external_exports.string() }))
|
|
23958
|
+
});
|
|
23959
|
+
var AddAppListingRequestSchema = external_exports.object({ spaceId: external_exports.string().max(64).nullable().default(null) }).strict();
|
|
23960
|
+
var AddAppListingResultSchema = external_exports.object({
|
|
23961
|
+
/** The app, installed (or updated, if it was). */
|
|
23962
|
+
app: external_exports.object({ id: external_exports.string(), name: external_exports.string() }),
|
|
23963
|
+
/** Its tab, where it was added. */
|
|
23964
|
+
tab: external_exports.object({ spaceId: external_exports.string(), tabId: external_exports.string() }).nullable(),
|
|
23965
|
+
steps: external_exports.array(AddedStepSchema)
|
|
23966
|
+
});
|
|
23967
|
+
|
|
23222
23968
|
// src/harness.ts
|
|
23223
23969
|
import { createHash, generateKeyPairSync, randomBytes, sign } from "node:crypto";
|
|
23224
23970
|
import { createServer } from "node:http";
|
|
@@ -23948,6 +24694,49 @@ async function startHarness({
|
|
|
23948
24694
|
};
|
|
23949
24695
|
}
|
|
23950
24696
|
|
|
24697
|
+
// src/publish.ts
|
|
24698
|
+
async function publish({
|
|
24699
|
+
url: url2,
|
|
24700
|
+
key,
|
|
24701
|
+
changes = "",
|
|
24702
|
+
fetchImpl = fetch
|
|
24703
|
+
}) {
|
|
24704
|
+
let origin;
|
|
24705
|
+
try {
|
|
24706
|
+
origin = new URL(url2).origin;
|
|
24707
|
+
} catch {
|
|
24708
|
+
return { ok: false, message: `\u201C${url2}\u201D isn't a web address (TANDS_URL).` };
|
|
24709
|
+
}
|
|
24710
|
+
let response;
|
|
24711
|
+
try {
|
|
24712
|
+
response = await fetchImpl(`${origin}/api/publish/app`, {
|
|
24713
|
+
method: "POST",
|
|
24714
|
+
headers: { authorization: `Bearer ${key}`, "content-type": "application/json" },
|
|
24715
|
+
body: JSON.stringify({ changes }),
|
|
24716
|
+
signal: AbortSignal.timeout(6e4)
|
|
24717
|
+
});
|
|
24718
|
+
} catch (error62) {
|
|
24719
|
+
return {
|
|
24720
|
+
ok: false,
|
|
24721
|
+
message: `${origin} can't be reached: ${error62 instanceof Error ? error62.message : String(error62)}`
|
|
24722
|
+
};
|
|
24723
|
+
}
|
|
24724
|
+
const json2 = await response.json().catch(() => null);
|
|
24725
|
+
if (!response.ok || !json2?.app || !json2.listing) {
|
|
24726
|
+
return {
|
|
24727
|
+
ok: false,
|
|
24728
|
+
message: json2?.error?.message ?? `${origin} answered HTTP ${response.status}.`
|
|
24729
|
+
};
|
|
24730
|
+
}
|
|
24731
|
+
return {
|
|
24732
|
+
ok: true,
|
|
24733
|
+
app: json2.app,
|
|
24734
|
+
version: json2.listing.latest.version,
|
|
24735
|
+
live: json2.listing.liveVersion,
|
|
24736
|
+
updated: Boolean(json2.updated)
|
|
24737
|
+
};
|
|
24738
|
+
}
|
|
24739
|
+
|
|
23951
24740
|
// src/scaffold.ts
|
|
23952
24741
|
import { existsSync } from "node:fs";
|
|
23953
24742
|
import { copyFile, mkdir, readdir, writeFile } from "node:fs/promises";
|
|
@@ -24212,7 +25001,7 @@ your manifest's address. See https://developer.tabsandspaces.dev.
|
|
|
24212
25001
|
`;
|
|
24213
25002
|
|
|
24214
25003
|
// src/bin.ts
|
|
24215
|
-
var version2 = true ? "0.
|
|
25004
|
+
var version2 = true ? "0.2.0" : "dev";
|
|
24216
25005
|
var USAGE = `tands: developer tools for Tabs & Spaces apps
|
|
24217
25006
|
|
|
24218
25007
|
tands new <folder> [--name "My app"] [--id my-app]
|
|
@@ -24227,13 +25016,21 @@ var USAGE = `tands: developer tools for Tabs & Spaces apps
|
|
|
24227
25016
|
choose, with your data in memory, your events and your actions.
|
|
24228
25017
|
Default: http://localhost:5480/tands.json
|
|
24229
25018
|
|
|
25019
|
+
tands publish [--changes "What changed"]
|
|
25020
|
+
Sends a new version of your app to the marketplace for review. Its
|
|
25021
|
+
workspace checks it for updates first, then sends it with its listing's
|
|
25022
|
+
words and images. Needs TANDS_URL (the workspace's address) and
|
|
25023
|
+
TANDS_PUBLISH_KEY (a key from Settings \u2192 Apps \u2192 Publish\u2026 there).
|
|
25024
|
+
|
|
24230
25025
|
tands --version
|
|
24231
25026
|
|
|
24232
25027
|
Docs: https://developer.tabsandspaces.dev`;
|
|
24233
25028
|
async function main(argv, {
|
|
24234
25029
|
cwd = process.env.INIT_CWD ?? process.cwd(),
|
|
24235
25030
|
out = (line) => console.log(line),
|
|
24236
|
-
err = (line) => console.error(line)
|
|
25031
|
+
err = (line) => console.error(line),
|
|
25032
|
+
env = process.env,
|
|
25033
|
+
fetchImpl = fetch
|
|
24237
25034
|
} = {}) {
|
|
24238
25035
|
const { values, positionals } = parseArgs({
|
|
24239
25036
|
args: argv,
|
|
@@ -24242,6 +25039,7 @@ async function main(argv, {
|
|
|
24242
25039
|
name: { type: "string" },
|
|
24243
25040
|
id: { type: "string" },
|
|
24244
25041
|
port: { type: "string" },
|
|
25042
|
+
changes: { type: "string" },
|
|
24245
25043
|
help: { type: "boolean", short: "h" },
|
|
24246
25044
|
version: { type: "boolean", short: "v" }
|
|
24247
25045
|
}
|
|
@@ -24334,6 +25132,29 @@ async function main(argv, {
|
|
|
24334
25132
|
return 1;
|
|
24335
25133
|
}
|
|
24336
25134
|
}
|
|
25135
|
+
case "publish": {
|
|
25136
|
+
const url2 = env.TANDS_URL?.trim();
|
|
25137
|
+
const key = env.TANDS_PUBLISH_KEY?.trim();
|
|
25138
|
+
if (!url2 || !key) {
|
|
25139
|
+
err("tands publish needs your workspace's address and a publishing key:");
|
|
25140
|
+
err(" TANDS_URL=https://your.workspace TANDS_PUBLISH_KEY=tas-pk-\u2026 npx tands publish");
|
|
25141
|
+
err("Make the key in the workspace: Settings \u2192 Apps \u2192 Publish\u2026 \u2192 Make a key.");
|
|
25142
|
+
return 1;
|
|
25143
|
+
}
|
|
25144
|
+
const sent = await publish({ url: url2, key, changes: values.changes ?? "", fetchImpl });
|
|
25145
|
+
if (!sent.ok) {
|
|
25146
|
+
err(sent.message);
|
|
25147
|
+
return 1;
|
|
25148
|
+
}
|
|
25149
|
+
out(
|
|
25150
|
+
`\u2713 Sent ${sent.app.name} (${sent.app.id}) version ${sent.version} to the marketplace for review.`
|
|
25151
|
+
);
|
|
25152
|
+
if (sent.updated) out(" Its workspace had an older manifest: it's up to date now.");
|
|
25153
|
+
out(
|
|
25154
|
+
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."
|
|
25155
|
+
);
|
|
25156
|
+
return 0;
|
|
25157
|
+
}
|
|
24337
25158
|
default:
|
|
24338
25159
|
err(`There's no command \u201C${command}\u201D.`);
|
|
24339
25160
|
out(USAGE);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tands",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Developer tools for Tabs & Spaces apps: `tands new`, `tands check
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Developer tools for Tabs & Spaces apps: `tands new`, `tands check`, `tands dev` (a local harness that stands in for a workspace) and `tands publish`.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Tabs & Spaces",
|
|
7
7
|
"homepage": "https://developer.tabsandspaces.dev/tools/",
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"test:coverage": "vitest run --coverage"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@tabs-and-spaces/sdk": "0.
|
|
41
|
+
"@tabs-and-spaces/sdk": "0.3.0",
|
|
42
42
|
"@tabs-and-spaces/types": "0.0.0",
|
|
43
43
|
"@types/node": "^26.6.3",
|
|
44
44
|
"@vitest/coverage-v8": "5.0.2",
|