@tomradex/plugin-sdk 0.0.0-stage → 0.2.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/im.js ADDED
@@ -0,0 +1,55 @@
1
+ import { z } from "zod";
2
+ export const IM_MARKDOWN_DIALECTS = ["plain", "markdown", "slack", "telegram-html", "discord", "lark", "dingtalk", "wecom"];
3
+ /** The capability fields only SDK 0.2.5 understands; declaring one needs `engines.sdk` of at least that. */
4
+ export const IM_CAPABILITIES_V2 = ["threads", "reactions", "typing", "stream", "select", "multiSelect", "textInput", "cards", "cardUpdate", "nativeCommands", "ephemeral", "markdown", "rateLimit"];
5
+ export const ImCapabilitiesSchema = z.object({
6
+ edit: z.boolean(),
7
+ buttons: z.boolean(),
8
+ inboundFiles: z.boolean(),
9
+ outboundFiles: z.boolean(),
10
+ maxTextLength: z.number().int().min(128),
11
+ maxFileBytes: z.number().int().min(0),
12
+ threads: z.boolean().optional(),
13
+ reactions: z.boolean().optional(),
14
+ typing: z.boolean().optional(),
15
+ stream: z.enum(["native", "edit", "none"]).optional(),
16
+ select: z.boolean().optional(),
17
+ multiSelect: z.boolean().optional(),
18
+ textInput: z.boolean().optional(),
19
+ cards: z.boolean().optional(),
20
+ cardUpdate: z.boolean().optional(),
21
+ nativeCommands: z.boolean().optional(),
22
+ ephemeral: z.boolean().optional(),
23
+ markdown: z.enum(IM_MARKDOWN_DIALECTS).optional(),
24
+ rateLimit: z.object({ messages: z.number().int().min(1), windowMs: z.number().int().min(1) }).optional()
25
+ });
26
+ export function resolveImCapabilities(caps) {
27
+ const parsed = ImCapabilitiesSchema.parse(caps);
28
+ return {
29
+ ...parsed,
30
+ threads: parsed.threads ?? false,
31
+ reactions: parsed.reactions ?? false,
32
+ typing: parsed.typing ?? false,
33
+ stream: parsed.stream ?? "none",
34
+ select: parsed.select ?? false,
35
+ multiSelect: parsed.multiSelect ?? false,
36
+ textInput: parsed.textInput ?? false,
37
+ cards: parsed.cards ?? false,
38
+ cardUpdate: parsed.cardUpdate ?? parsed.edit,
39
+ nativeCommands: parsed.nativeCommands ?? false,
40
+ ephemeral: parsed.ephemeral ?? false,
41
+ markdown: parsed.markdown ?? "markdown"
42
+ };
43
+ }
44
+ /** Only a known-unsent operation is safe to retry; unknown failures remain uncertain. */
45
+ export class ImDeliveryError extends Error {
46
+ disposition;
47
+ retryAfterMs;
48
+ constructor(message, disposition, retryAfterMs) {
49
+ super(message);
50
+ this.disposition = disposition;
51
+ this.retryAfterMs = retryAfterMs;
52
+ this.name = "ImDeliveryError";
53
+ }
54
+ }
55
+ //# sourceMappingURL=im.js.map
@@ -0,0 +1,14 @@
1
+ export * from "./contract.js";
2
+ export { SDK_PLUGIN, ToolInputError, definePlugin, defineTool, isSdkPlugin, requireProjectId, type PluginSpec, type SdkPlugin } from "./define.js";
3
+ export * from "./manifest.js";
4
+ export * from "./language.js";
5
+ export * from "./exports.js";
6
+ export { compareVersions, isRange, isVersion, parseVersion, satisfies } from "./semver.js";
7
+ export type { Author, Board, CreateItemInput, FieldDef, FieldOption, FieldType, FieldValue, FilterClause, Item, ItemKind, ItemStatus, ListItemsFilter, OplogEntity, OplogEntry, OplogOp, Patch, Priority, Project, SortKey, View, WorkType } from "@tomradex/domain";
8
+ export * from "./im.js";
9
+ export * from "./web-search.js";
10
+ export * from "./models.js";
11
+ export * from "./terminals.js";
12
+ export { CLI_HOOK_COVERAGE, CLI_HOOK_EVENTS } from "./cli-hooks.js";
13
+ export * from "./im-markdown.js";
14
+ //# sourceMappingURL=index.d.ts.map
package/dist/index.js ADDED
@@ -0,0 +1,13 @@
1
+ export * from "./contract.js";
2
+ export { SDK_PLUGIN, ToolInputError, definePlugin, defineTool, isSdkPlugin, requireProjectId } from "./define.js";
3
+ export * from "./manifest.js";
4
+ export * from "./language.js";
5
+ export * from "./exports.js";
6
+ export { compareVersions, isRange, isVersion, parseVersion, satisfies } from "./semver.js";
7
+ export * from "./im.js";
8
+ export * from "./web-search.js";
9
+ export * from "./models.js";
10
+ export * from "./terminals.js";
11
+ export { CLI_HOOK_COVERAGE, CLI_HOOK_EVENTS } from "./cli-hooks.js";
12
+ export * from "./im-markdown.js";
13
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,48 @@
1
+ import { z } from "zod";
2
+ import { type Dictionaries } from "@tomradex/domain";
3
+ import type { Locale } from "./contract.js";
4
+ export { languageCoverage, pickLanguage, slotsOf, type Dictionaries, type LanguageCoverage } from "@tomradex/domain";
5
+ /**
6
+ * A plugin's own words in the app's language (`ctx.settings.locale()`): the
7
+ * closest language it has, picked as Tomrade picks its own (zh-TW finds
8
+ * zh-Hant, never zh), and English when it has none of that language.
9
+ */
10
+ export declare function wordsFor<T>(locale: Locale, words: {
11
+ en: T;
12
+ } & Partial<Record<Locale, T>>): T;
13
+ /**
14
+ * A language pack: data only. The manifest names each language a plugin adds
15
+ * and the JSON file holding its words; nothing in it runs, and every string
16
+ * is shown as plain text in place of the English it translates.
17
+ */
18
+ /** The largest pack file Tomrade reads, and the longest single template. */
19
+ export declare const LANGUAGE_PACK_LIMITS: {
20
+ fileBytes: number;
21
+ templateChars: number;
22
+ languages: number;
23
+ };
24
+ export declare const LanguageEntrySchema: z.ZodObject<{
25
+ tag: z.ZodString;
26
+ name: z.ZodString;
27
+ file: z.ZodString;
28
+ }, z.core.$strip>;
29
+ export type LanguageEntry = z.output<typeof LanguageEntrySchema>;
30
+ /**
31
+ * What a pack file holds: `ui` translates the interface's dictionaries,
32
+ * `backend` the backend's books, each `namespace → key → template`, keyed as
33
+ * English is. A template names its values as `{{slot}}`, exactly the slots
34
+ * English's does; an entry that does not, or a key English lacks, reads English.
35
+ */
36
+ export declare const LanguagePackFileSchema: z.ZodObject<{
37
+ ui: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>;
38
+ backend: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodRecord<z.ZodString, z.ZodString>>>;
39
+ }, z.core.$strict>;
40
+ export interface LanguagePack {
41
+ tag: string;
42
+ name: string;
43
+ ui: Dictionaries;
44
+ backend: Dictionaries;
45
+ }
46
+ /** A pack file's text checked against the format; throws with the first problem. */
47
+ export declare function parseLanguagePack(entry: LanguageEntry, text: string): LanguagePack;
48
+ //# sourceMappingURL=language.d.ts.map
@@ -0,0 +1,62 @@
1
+ import { z } from "zod";
2
+ import { canonicalTag, pickLanguage } from "@tomradex/domain";
3
+ export { languageCoverage, pickLanguage, slotsOf } from "@tomradex/domain";
4
+ /**
5
+ * A plugin's own words in the app's language (`ctx.settings.locale()`): the
6
+ * closest language it has, picked as Tomrade picks its own (zh-TW finds
7
+ * zh-Hant, never zh), and English when it has none of that language.
8
+ */
9
+ export function wordsFor(locale, words) {
10
+ return words[pickLanguage(locale, Object.keys(words)) ?? "en"] ?? words.en;
11
+ }
12
+ /**
13
+ * A language pack: data only. The manifest names each language a plugin adds
14
+ * and the JSON file holding its words; nothing in it runs, and every string
15
+ * is shown as plain text in place of the English it translates.
16
+ */
17
+ /** The largest pack file Tomrade reads, and the longest single template. */
18
+ export const LANGUAGE_PACK_LIMITS = { fileBytes: 2 * 1024 * 1024, templateChars: 4_000, languages: 16 };
19
+ export const LanguageEntrySchema = z.object({
20
+ /** A BCP 47 tag: "pt-BR", "ko", "zh-Hant". */
21
+ tag: z
22
+ .string()
23
+ .refine((t) => canonicalTag(t) === t, "a canonical BCP 47 language tag such as \"pt-BR\" or \"ko\"")
24
+ .refine((t) => t !== "en", "English is what every pack translates; it cannot be replaced"),
25
+ /** The language's name in itself, as Settings → Language lists it: "Português (Brasil)". */
26
+ name: z.string().trim().min(1).max(60),
27
+ /** The pack's words, a JSON file inside the plugin: "languages/pt-BR.json". */
28
+ file: z
29
+ .string()
30
+ .regex(/^[A-Za-z0-9_-][A-Za-z0-9._/-]*\.json$/, "a relative path to a .json file inside the plugin")
31
+ .refine((f) => !f.split("/").includes(".."), "the file must stay inside the plugin")
32
+ });
33
+ const Words = z.record(z.string().min(1), z.record(z.string().min(1), z.string().max(LANGUAGE_PACK_LIMITS.templateChars)));
34
+ /**
35
+ * What a pack file holds: `ui` translates the interface's dictionaries,
36
+ * `backend` the backend's books, each `namespace → key → template`, keyed as
37
+ * English is. A template names its values as `{{slot}}`, exactly the slots
38
+ * English's does; an entry that does not, or a key English lacks, reads English.
39
+ */
40
+ export const LanguagePackFileSchema = z
41
+ .object({
42
+ ui: Words.default({}),
43
+ backend: Words.default({})
44
+ })
45
+ .strict();
46
+ /** A pack file's text checked against the format; throws with the first problem. */
47
+ export function parseLanguagePack(entry, text) {
48
+ let raw;
49
+ try {
50
+ raw = JSON.parse(text);
51
+ }
52
+ catch (error) {
53
+ throw new Error(`${entry.file} is not JSON: ${error instanceof Error ? error.message : String(error)}`);
54
+ }
55
+ const parsed = LanguagePackFileSchema.safeParse(raw);
56
+ if (!parsed.success) {
57
+ const issue = parsed.error.issues[0];
58
+ throw new Error(`${entry.file} ${issue.path.join(".") || "(root)"}: ${issue.message}`);
59
+ }
60
+ return { tag: entry.tag, name: entry.name, ui: parsed.data.ui, backend: parsed.data.backend };
61
+ }
62
+ //# sourceMappingURL=language.js.map
@@ -0,0 +1,202 @@
1
+ import { z } from "zod";
2
+ /** The contract this SDK describes. A plugin's `engines.sdk` range is checked against the host's. */
3
+ export declare const SDK_CONTRACT = "0.2.16";
4
+ export declare const PLUGIN_ID: RegExp;
5
+ export declare const NAMESPACE: RegExp;
6
+ /** The app's own names, and the official publisher's. */
7
+ export declare const RESERVED_NAMESPACES: Set<string>;
8
+ export declare const OFFICIAL_PUBLISHER = "tomrade";
9
+ export declare const PermissionsSchema: z.ZodDefault<z.ZodObject<{
10
+ network: z.ZodDefault<z.ZodArray<z.ZodString>>;
11
+ fs: z.ZodDefault<z.ZodEnum<{
12
+ none: "none";
13
+ project: "project";
14
+ any: "any";
15
+ plugin: "plugin";
16
+ }>>;
17
+ exec: z.ZodDefault<z.ZodBoolean>;
18
+ window: z.ZodDefault<z.ZodBoolean>;
19
+ why: z.ZodOptional<z.ZodObject<{
20
+ network: z.ZodOptional<z.ZodString>;
21
+ fs: z.ZodOptional<z.ZodString>;
22
+ exec: z.ZodOptional<z.ZodString>;
23
+ window: z.ZodOptional<z.ZodString>;
24
+ full: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strip>>;
26
+ }, z.core.$strip>>;
27
+ export declare const ContributesSchema: z.ZodObject<{
28
+ tools: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
29
+ commands: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
30
+ badges: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
31
+ statusItems: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
32
+ sections: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
33
+ graphs: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
34
+ sidebarModules: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
35
+ playbooks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
36
+ settings: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
37
+ surfaces: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
38
+ tables: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
39
+ imAdapters: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
40
+ webSearchProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
41
+ agentHooks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
42
+ modelProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
43
+ }, z.core.$strip>;
44
+ /**
45
+ * One row a full-mode plugin lays over Tomrade's composition, merged by id like a
46
+ * `tomrade.patch.yml` row: an app row's id switches it off, replaces its plugin or
47
+ * changes its config; a new id (carrying the namespace) adds a row. A relative
48
+ * `name` is a file inside the package.
49
+ */
50
+ export declare const RowSchema: z.ZodObject<{
51
+ id: z.ZodString;
52
+ name: z.ZodOptional<z.ZodString>;
53
+ title: z.ZodOptional<z.ZodString>;
54
+ config: z.ZodOptional<z.ZodUnknown>;
55
+ disabled: z.ZodOptional<z.ZodBoolean>;
56
+ }, z.core.$strip>;
57
+ export type ManifestRow = z.output<typeof RowSchema>;
58
+ export declare const ManifestSchema: z.ZodObject<{
59
+ id: z.ZodString;
60
+ namespace: z.ZodOptional<z.ZodString>;
61
+ version: z.ZodString;
62
+ title: z.ZodString;
63
+ description: z.ZodDefault<z.ZodString>;
64
+ license: z.ZodString;
65
+ repository: z.ZodOptional<z.ZodURL>;
66
+ homepage: z.ZodOptional<z.ZodURL>;
67
+ author: z.ZodOptional<z.ZodObject<{
68
+ name: z.ZodString;
69
+ url: z.ZodOptional<z.ZodURL>;
70
+ }, z.core.$strip>>;
71
+ engines: z.ZodObject<{
72
+ sdk: z.ZodString;
73
+ tomrade: z.ZodOptional<z.ZodString>;
74
+ }, z.core.$strip>;
75
+ context: z.ZodDefault<z.ZodEnum<{
76
+ full: "full";
77
+ sdk: "sdk";
78
+ }>>;
79
+ rows: z.ZodDefault<z.ZodArray<z.ZodObject<{
80
+ id: z.ZodString;
81
+ name: z.ZodOptional<z.ZodString>;
82
+ title: z.ZodOptional<z.ZodString>;
83
+ config: z.ZodOptional<z.ZodUnknown>;
84
+ disabled: z.ZodOptional<z.ZodBoolean>;
85
+ }, z.core.$strip>>>;
86
+ main: z.ZodDefault<z.ZodNullable<z.ZodString>>;
87
+ icon: z.ZodOptional<z.ZodString>;
88
+ languages: z.ZodDefault<z.ZodArray<z.ZodObject<{
89
+ tag: z.ZodString;
90
+ name: z.ZodString;
91
+ file: z.ZodString;
92
+ }, z.core.$strip>>>;
93
+ permissions: z.ZodDefault<z.ZodObject<{
94
+ network: z.ZodDefault<z.ZodArray<z.ZodString>>;
95
+ fs: z.ZodDefault<z.ZodEnum<{
96
+ none: "none";
97
+ project: "project";
98
+ any: "any";
99
+ plugin: "plugin";
100
+ }>>;
101
+ exec: z.ZodDefault<z.ZodBoolean>;
102
+ window: z.ZodDefault<z.ZodBoolean>;
103
+ why: z.ZodOptional<z.ZodObject<{
104
+ network: z.ZodOptional<z.ZodString>;
105
+ fs: z.ZodOptional<z.ZodString>;
106
+ exec: z.ZodOptional<z.ZodString>;
107
+ window: z.ZodOptional<z.ZodString>;
108
+ full: z.ZodOptional<z.ZodString>;
109
+ }, z.core.$strip>>;
110
+ }, z.core.$strip>>;
111
+ inject: z.ZodDefault<z.ZodArray<z.ZodEnum<{
112
+ items: "items";
113
+ fields: "fields";
114
+ boards: "boards";
115
+ views: "views";
116
+ projects: "projects";
117
+ db: "db";
118
+ settings: "settings";
119
+ tools: "tools";
120
+ contributions: "contributions";
121
+ agent: "agent";
122
+ surfaces: "surfaces";
123
+ alerts: "alerts";
124
+ oplog: "oplog";
125
+ im: "im";
126
+ webSearch: "webSearch";
127
+ terminals: "terminals";
128
+ models: "models";
129
+ }>>>;
130
+ optional: z.ZodDefault<z.ZodArray<z.ZodEnum<{
131
+ items: "items";
132
+ fields: "fields";
133
+ boards: "boards";
134
+ views: "views";
135
+ projects: "projects";
136
+ db: "db";
137
+ settings: "settings";
138
+ tools: "tools";
139
+ contributions: "contributions";
140
+ agent: "agent";
141
+ surfaces: "surfaces";
142
+ alerts: "alerts";
143
+ oplog: "oplog";
144
+ im: "im";
145
+ webSearch: "webSearch";
146
+ terminals: "terminals";
147
+ models: "models";
148
+ }>>>;
149
+ contributes: z.ZodDefault<z.ZodObject<{
150
+ tools: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
151
+ commands: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
152
+ badges: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
153
+ statusItems: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
154
+ sections: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
155
+ graphs: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
156
+ sidebarModules: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
157
+ playbooks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
158
+ settings: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
159
+ surfaces: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
160
+ tables: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
161
+ imAdapters: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
162
+ webSearchProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
163
+ agentHooks: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
164
+ modelProviders: z.ZodOptional<z.ZodDefault<z.ZodArray<z.ZodString>>>;
165
+ }, z.core.$strip>>;
166
+ authoredBy: z.ZodDefault<z.ZodEnum<{
167
+ agent: "agent";
168
+ human: "human";
169
+ mixed: "mixed";
170
+ }>>;
171
+ forkOf: z.ZodOptional<z.ZodObject<{
172
+ id: z.ZodString;
173
+ version: z.ZodString;
174
+ }, z.core.$strip>>;
175
+ }, z.core.$strip>;
176
+ export type Manifest = z.output<typeof ManifestSchema>;
177
+ export type ManifestInput = z.input<typeof ManifestSchema>;
178
+ export type Permissions = z.output<typeof PermissionsSchema>;
179
+ export declare const MANIFEST_FILE = "tomrade-plugin.json";
180
+ export declare function parseManifest(raw: unknown): Manifest;
181
+ export declare const publisherOf: (id: string) => string;
182
+ export declare const nameOf: (id: string) => string;
183
+ /** The prefix every id the plugin registers carries, and the name of its tables. */
184
+ export declare const namespaceOf: (manifest: Pick<Manifest, "id" | "namespace">) => string;
185
+ export declare const ownsId: (namespace: string, id: string) => boolean;
186
+ export declare function assertOwned(namespace: string, id: string, kind: string): void;
187
+ export type RiskLevel = "low" | "elevated" | "high";
188
+ export interface Risk {
189
+ level: RiskLevel;
190
+ /** Stable keys the UI words: `full`, `rows`, `exec`, `fs-any`, `network-any`, `network`, `fs-project`, `db`, `models`, `window`. */
191
+ reasons: string[];
192
+ }
193
+ /**
194
+ * High risk is labelled, not refused: running with Tomrade's whole context or
195
+ * changing its own rows, running programs, reading anywhere on disk, or talking
196
+ * to any host. Elevated is what a person should still see.
197
+ */
198
+ export declare function riskOf(manifest: Pick<Manifest, "permissions" | "inject" | "optional"> & Partial<Pick<Manifest, "context" | "rows">>): Risk;
199
+ /** OSI-approved licenses the store accepts without a second look. */
200
+ export declare const OSI_LICENSES: Set<string>;
201
+ export declare const isOpenSource: (license: string) => boolean;
202
+ //# sourceMappingURL=manifest.d.ts.map
@@ -0,0 +1,168 @@
1
+ import { z } from "zod";
2
+ import { LANGUAGE_PACK_LIMITS, LanguageEntrySchema } from "./language.js";
3
+ import { isRange, isVersion } from "./semver.js";
4
+ /** The contract this SDK describes. A plugin's `engines.sdk` range is checked against the host's. */
5
+ export const SDK_CONTRACT = "0.2.16";
6
+ export const PLUGIN_ID = /^[a-z0-9][a-z0-9-]{0,38}\/[a-z0-9][a-z0-9-]{0,63}$/;
7
+ export const NAMESPACE = /^[a-z][a-z0-9_]{1,31}$/;
8
+ /** The app's own names, and the official publisher's. */
9
+ export const RESERVED_NAMESPACES = new Set([
10
+ "agent", "alerts", "archify", "boards", "code_link", "contributions", "core", "db", "deps", "deps_graph", "fields", "files", "hub", "im", "ipc", "items",
11
+ "llm", "mcp", "oplog", "pet", "plugin", "plugins", "pomodoro", "projects", "repos", "settings", "storage", "surfaces", "system", "tomrade", "tools", "views", "write"
12
+ ]);
13
+ export const OFFICIAL_PUBLISHER = "tomrade";
14
+ const SERVICE = z.enum(["items", "fields", "boards", "views", "projects", "db", "settings", "tools", "contributions", "agent", "surfaces", "alerts", "oplog", "im", "webSearch", "terminals", "models"]);
15
+ export const PermissionsSchema = z
16
+ .object({
17
+ /** Hosts the plugin talks to; `*` means any. */
18
+ network: z.array(z.string().min(1)).default([]),
19
+ /** `plugin`: its own data directory. `project`: the project folders. `any`: anywhere on disk. */
20
+ fs: z.enum(["none", "plugin", "project", "any"]).default("none"),
21
+ /** Starts other programs. */
22
+ exec: z.boolean().default(false),
23
+ /** SDK 0.2.11: opens working windows of its own (a surface of kind "window"), which take keys like any app window. */
24
+ window: z.boolean().default(false),
25
+ /** Why the plugin needs a wide permission, in a sentence a person installing it can judge. Official plugins must say. */
26
+ why: z.object({ network: z.string().min(1), fs: z.string().min(1), exec: z.string().min(1), window: z.string().min(1), full: z.string().min(1) }).partial().optional()
27
+ })
28
+ .default({ network: [], fs: "none", exec: false, window: false });
29
+ export const ContributesSchema = z
30
+ .object({
31
+ tools: z.array(z.string()).default([]),
32
+ commands: z.array(z.string()).default([]),
33
+ badges: z.array(z.string()).default([]),
34
+ statusItems: z.array(z.string()).default([]),
35
+ sections: z.array(z.string()).default([]),
36
+ graphs: z.array(z.string()).default([]),
37
+ sidebarModules: z.array(z.string()).default([]),
38
+ playbooks: z.array(z.string()).default([]),
39
+ settings: z.array(z.string()).default([]),
40
+ surfaces: z.array(z.string()).default([]),
41
+ tables: z.array(z.string()).default([]),
42
+ imAdapters: z.array(z.string()).default([]),
43
+ webSearchProviders: z.array(z.string()).default([]),
44
+ /** SDK 0.2.15: the CLI agent hook events it hears. */
45
+ agentHooks: z.array(z.string()).default([]),
46
+ /** SDK 0.2.16: the model providers it registers. */
47
+ modelProviders: z.array(z.string()).default([])
48
+ })
49
+ .partial();
50
+ /**
51
+ * One row a full-mode plugin lays over Tomrade's composition, merged by id like a
52
+ * `tomrade.patch.yml` row: an app row's id switches it off, replaces its plugin or
53
+ * changes its config; a new id (carrying the namespace) adds a row. A relative
54
+ * `name` is a file inside the package.
55
+ */
56
+ export const RowSchema = z
57
+ .object({
58
+ id: z.string().min(1),
59
+ name: z.string().min(1).optional(),
60
+ title: z.string().min(1).optional(),
61
+ config: z.unknown().optional(),
62
+ disabled: z.boolean().optional()
63
+ })
64
+ .refine((row) => row.name !== undefined || row.config !== undefined || row.disabled !== undefined, "a row changes something: name, config or disabled");
65
+ export const ManifestSchema = z.object({
66
+ id: z.string().regex(PLUGIN_ID, "publisher/name, lowercase letters, digits and dashes"),
67
+ namespace: z.string().regex(NAMESPACE, "lowercase letters, digits and underscores, 2–32 long, starting with a letter").optional(),
68
+ version: z.string().refine(isVersion, "a semantic version such as 1.2.0"),
69
+ title: z.string().min(1).max(60),
70
+ description: z.string().max(500).default(""),
71
+ /** An SPDX identifier of an OSI-approved license. */
72
+ license: z.string().min(1),
73
+ repository: z.url().optional(),
74
+ homepage: z.url().optional(),
75
+ author: z.object({ name: z.string().min(1), url: z.url().optional() }).optional(),
76
+ engines: z.object({
77
+ sdk: z.string().refine(isRange, "a version range such as ^0.2.0"),
78
+ /** SDK 0.2.13: the Tomrade versions a full-mode plugin works with; it reaches internals the SDK range does not cover. */
79
+ tomrade: z.string().refine(isRange, "a version range such as >=0.13.0 <0.14.0").optional()
80
+ }),
81
+ /**
82
+ * SDK 0.2.13: what `main` receives. `"sdk"` is a `definePlugin` plugin behind the async facade;
83
+ * `"full"` is a plain cordis plugin given Tomrade's whole Context, as the app's own modules are.
84
+ */
85
+ context: z.enum(["sdk", "full"]).default("sdk"),
86
+ /** SDK 0.2.13, full mode only: rows laid over Tomrade's own, as `tomrade.patch.yml` rows are. */
87
+ rows: z.array(RowSchema).default([]),
88
+ /** The built code; `null` for a plugin that is data only, such as a language pack. */
89
+ main: z.string().nullable().default("dist/index.js"),
90
+ icon: z.string().optional(),
91
+ /** Languages the plugin adds, each a JSON file of words (SDK 0.2.8). */
92
+ languages: z.array(LanguageEntrySchema).max(LANGUAGE_PACK_LIMITS.languages).default([]),
93
+ permissions: PermissionsSchema,
94
+ /** Stamped by `tomrade-plugin build` from the exported plugin. */
95
+ inject: z.array(SERVICE).default([]),
96
+ optional: z.array(SERVICE).default([]),
97
+ contributes: ContributesSchema.default({}),
98
+ authoredBy: z.enum(["human", "agent", "mixed"]).default("human"),
99
+ /** A fork names what it came from; the store credits the original. */
100
+ forkOf: z.object({ id: z.string().regex(PLUGIN_ID), version: z.string() }).optional()
101
+ });
102
+ export const MANIFEST_FILE = "tomrade-plugin.json";
103
+ export function parseManifest(raw) {
104
+ const manifest = ManifestSchema.parse(raw);
105
+ const ns = namespaceOf(manifest);
106
+ if (!NAMESPACE.test(ns))
107
+ throw new Error(`${manifest.id}: the name part does not make a valid namespace ("${ns}"); set "namespace" in ${MANIFEST_FILE}`);
108
+ if (RESERVED_NAMESPACES.has(ns) && publisherOf(manifest.id) !== OFFICIAL_PUBLISHER)
109
+ throw new Error(`${manifest.id}: namespace "${ns}" is reserved`);
110
+ if (manifest.main === null && !manifest.languages.length)
111
+ throw new Error(`${manifest.id}: "main" is null, so the plugin has no code; it must add a language under "languages"`);
112
+ if (manifest.context === "full") {
113
+ if (manifest.main === null)
114
+ throw new Error(`${manifest.id}: a full-mode plugin needs "main", the cordis plugin it runs`);
115
+ if (!manifest.engines.tomrade)
116
+ throw new Error(`${manifest.id}: a full-mode plugin names the Tomrade versions it works with in "engines.tomrade"`);
117
+ if (manifest.languages.length)
118
+ throw new Error(`${manifest.id}: a full-mode plugin adds its languages from its own code (ctx.settings.addLanguage), not under "languages"`);
119
+ }
120
+ else if (manifest.rows.length)
121
+ throw new Error(`${manifest.id}: "rows" change Tomrade's own composition, which takes "context": "full"`);
122
+ const ids = manifest.rows.map((r) => r.id);
123
+ const repeated = ids.find((id, i) => ids.indexOf(id) !== i);
124
+ if (repeated)
125
+ throw new Error(`${manifest.id}: the row "${repeated}" is listed twice`);
126
+ const tags = manifest.languages.map((l) => l.tag);
127
+ const twice = tags.find((t, i) => tags.indexOf(t) !== i);
128
+ if (twice)
129
+ throw new Error(`${manifest.id}: the language "${twice}" is listed twice`);
130
+ return manifest;
131
+ }
132
+ export const publisherOf = (id) => id.split("/")[0];
133
+ export const nameOf = (id) => id.split("/")[1] ?? id;
134
+ /** The prefix every id the plugin registers carries, and the name of its tables. */
135
+ export const namespaceOf = (manifest) => manifest.namespace ?? nameOf(manifest.id).replace(/-/g, "_");
136
+ export const ownsId = (namespace, id) => id === namespace || [".", "-", "_"].some((sep) => id.startsWith(namespace + sep));
137
+ export function assertOwned(namespace, id, kind) {
138
+ if (!ownsId(namespace, id))
139
+ throw new Error(`${kind} "${id}" must be named "${namespace}" or start with "${namespace}." "${namespace}-" or "${namespace}_"`);
140
+ }
141
+ /**
142
+ * High risk is labelled, not refused: running with Tomrade's whole context or
143
+ * changing its own rows, running programs, reading anywhere on disk, or talking
144
+ * to any host. Elevated is what a person should still see.
145
+ */
146
+ export function riskOf(manifest) {
147
+ const p = manifest.permissions;
148
+ const full = manifest.context === "full";
149
+ const high = [full && "full", full && manifest.rows?.length && "rows", p.exec && "exec", p.fs === "any" && "fs-any", p.network.includes("*") && "network-any"].filter(Boolean);
150
+ const elevated = [
151
+ p.network.length && !p.network.includes("*") && "network",
152
+ p.fs === "project" && "fs-project",
153
+ [...manifest.inject, ...manifest.optional].includes("db") && "db",
154
+ // A model provider reads the conversations of the profiles on it, and their keys.
155
+ [...manifest.inject, ...manifest.optional].includes("models") && "models",
156
+ // A window that takes keys could pass itself off as another app's: worth a look before installing.
157
+ p.window && "window"
158
+ ].filter(Boolean);
159
+ return { level: high.length ? "high" : elevated.length ? "elevated" : "low", reasons: [...high, ...elevated] };
160
+ }
161
+ /** OSI-approved licenses the store accepts without a second look. */
162
+ export const OSI_LICENSES = new Set([
163
+ "0BSD", "AGPL-3.0", "AGPL-3.0-only", "AGPL-3.0-or-later", "Apache-2.0", "Artistic-2.0", "BSD-2-Clause", "BSD-3-Clause", "BSL-1.0", "CDDL-1.0", "EPL-1.0", "EPL-2.0",
164
+ "EUPL-1.2", "GPL-2.0", "GPL-2.0-only", "GPL-2.0-or-later", "GPL-3.0", "GPL-3.0-only", "GPL-3.0-or-later", "ISC", "LGPL-2.1", "LGPL-2.1-only", "LGPL-2.1-or-later",
165
+ "LGPL-3.0", "LGPL-3.0-only", "LGPL-3.0-or-later", "MIT", "MIT-0", "MPL-2.0", "MS-PL", "MulanPSL-2.0", "NCSA", "OSL-3.0", "PostgreSQL", "Unlicense", "UPL-1.0", "Zlib"
166
+ ]);
167
+ export const isOpenSource = (license) => license.split(/\s+OR\s+/i).some((l) => OSI_LICENSES.has(l.replace(/[()]/g, "").trim()));
168
+ //# sourceMappingURL=manifest.js.map