@neurosquad/card-sdk 1.0.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/LICENSE +21 -0
- package/README.md +431 -0
- package/dist/card-sdk.js +5266 -0
- package/dist/cli.js +2838 -0
- package/dist/react.js +257 -0
- package/dist/testing.js +1150 -0
- package/dist/types/client/base64.d.ts +7 -0
- package/dist/types/client/card.d.ts +653 -0
- package/dist/types/client/channel.d.ts +49 -0
- package/dist/types/client/coalesce.d.ts +14 -0
- package/dist/types/client/connect.d.ts +39 -0
- package/dist/types/client/errors.d.ts +46 -0
- package/dist/types/client/helpers.d.ts +25 -0
- package/dist/types/client/net.d.ts +111 -0
- package/dist/types/client/theme.d.ts +31 -0
- package/dist/types/client/toolResult.d.ts +16 -0
- package/dist/types/contract/api.d.ts +812 -0
- package/dist/types/contract/index.d.ts +11 -0
- package/dist/types/contract/jsonSchema.d.ts +88 -0
- package/dist/types/contract/localized.d.ts +10 -0
- package/dist/types/contract/manifest.d.ts +179 -0
- package/dist/types/contract/network.d.ts +48 -0
- package/dist/types/contract/permissions.d.ts +180 -0
- package/dist/types/contract/ports.d.ts +237 -0
- package/dist/types/contract/protocol.d.ts +74 -0
- package/dist/types/contract/source.d.ts +111 -0
- package/dist/types/contract/theme.d.ts +21 -0
- package/dist/types/contract/version.d.ts +126 -0
- package/dist/types/i18n.d.ts +50 -0
- package/dist/types/index.d.ts +28 -0
- package/dist/types/react/index.d.ts +132 -0
- package/dist/types/testing/index.d.ts +7 -0
- package/dist/types/testing/mockHost.d.ts +315 -0
- package/dist/types/version.d.ts +2 -0
- package/dist/ui.css +581 -0
- package/package.json +77 -0
- package/schema/neurosquad-card.v1.json +434 -0
- package/templates/react/README.md +34 -0
- package/templates/react/_gitignore +10 -0
- package/templates/react/icon.png +0 -0
- package/templates/react/index.html +12 -0
- package/templates/react/neurosquad-card.json +94 -0
- package/templates/react/package.json +25 -0
- package/templates/react/src/App.tsx +131 -0
- package/templates/react/src/i18n.ts +61 -0
- package/templates/react/src/main.tsx +77 -0
- package/templates/react/src/styles.css +42 -0
- package/templates/react/tsconfig.json +18 -0
- package/templates/react/vite.config.ts +19 -0
- package/templates/vanilla/README.md +37 -0
- package/templates/vanilla/_gitignore +6 -0
- package/templates/vanilla/icon.png +0 -0
- package/templates/vanilla/index.html +41 -0
- package/templates/vanilla/main.js +256 -0
- package/templates/vanilla/neurosquad-card.json +94 -0
- package/templates/vanilla/style.css +41 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './version.js';
|
|
2
|
+
export * from './jsonSchema.js';
|
|
3
|
+
export * from './localized.js';
|
|
4
|
+
export * from './network.js';
|
|
5
|
+
export * from './permissions.js';
|
|
6
|
+
export * from './ports.js';
|
|
7
|
+
export * from './theme.js';
|
|
8
|
+
export * from './source.js';
|
|
9
|
+
export * from './manifest.js';
|
|
10
|
+
export * from './protocol.js';
|
|
11
|
+
export * from './api.js';
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
export type JsonPrimitiveType = 'string' | 'number' | 'integer' | 'boolean' | 'object' | 'array' | 'null';
|
|
2
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | {
|
|
3
|
+
[key: string]: JsonValue;
|
|
4
|
+
};
|
|
5
|
+
export declare const CARD_STRING_FORMATS: readonly ["uri", "date-time", "date", "email", "color", "uuid"];
|
|
6
|
+
export type CardStringFormat = (typeof CARD_STRING_FORMATS)[number];
|
|
7
|
+
export interface CardJsonSchema {
|
|
8
|
+
$id?: string;
|
|
9
|
+
$schema?: string;
|
|
10
|
+
$comment?: string;
|
|
11
|
+
$ref?: string;
|
|
12
|
+
$defs?: Record<string, CardJsonSchema>;
|
|
13
|
+
title?: string;
|
|
14
|
+
description?: string;
|
|
15
|
+
default?: unknown;
|
|
16
|
+
examples?: unknown[];
|
|
17
|
+
deprecated?: boolean;
|
|
18
|
+
readOnly?: boolean;
|
|
19
|
+
writeOnly?: boolean;
|
|
20
|
+
type?: JsonPrimitiveType | readonly JsonPrimitiveType[];
|
|
21
|
+
enum?: readonly unknown[];
|
|
22
|
+
const?: unknown;
|
|
23
|
+
properties?: Record<string, CardJsonSchema>;
|
|
24
|
+
required?: readonly string[];
|
|
25
|
+
additionalProperties?: boolean | CardJsonSchema;
|
|
26
|
+
minProperties?: number;
|
|
27
|
+
maxProperties?: number;
|
|
28
|
+
items?: CardJsonSchema;
|
|
29
|
+
minItems?: number;
|
|
30
|
+
maxItems?: number;
|
|
31
|
+
uniqueItems?: boolean;
|
|
32
|
+
minLength?: number;
|
|
33
|
+
maxLength?: number;
|
|
34
|
+
/** Only in the host's own schemas — card-declared schemas may not use it (checkCardSchema). */
|
|
35
|
+
pattern?: string;
|
|
36
|
+
format?: CardStringFormat;
|
|
37
|
+
minimum?: number;
|
|
38
|
+
maximum?: number;
|
|
39
|
+
exclusiveMinimum?: number;
|
|
40
|
+
exclusiveMaximum?: number;
|
|
41
|
+
multipleOf?: number;
|
|
42
|
+
anyOf?: readonly CardJsonSchema[];
|
|
43
|
+
oneOf?: readonly CardJsonSchema[];
|
|
44
|
+
}
|
|
45
|
+
export interface SchemaIssue {
|
|
46
|
+
/** JSON-pointer-like location in the value (`/ports/inputs/0/id`), `''` for the root. */
|
|
47
|
+
path: string;
|
|
48
|
+
keyword: string;
|
|
49
|
+
message: string;
|
|
50
|
+
}
|
|
51
|
+
export interface ValidateOptions {
|
|
52
|
+
/** Stop after this many issues (default 20). */
|
|
53
|
+
maxIssues?: number;
|
|
54
|
+
/** Work budget in schema-node visits (default 200 000). Exhausting it is an issue, never a hang. */
|
|
55
|
+
budget?: number;
|
|
56
|
+
/** Deepest value nesting accepted (default 64). */
|
|
57
|
+
maxDepth?: number;
|
|
58
|
+
}
|
|
59
|
+
/** JSON with object keys sorted — equality and uniqueness checks. */
|
|
60
|
+
export declare function stableStringify(value: unknown): string;
|
|
61
|
+
export declare function jsonEqual(a: unknown, b: unknown): boolean;
|
|
62
|
+
/**
|
|
63
|
+
* True when `value` is plain JSON: finite numbers, plain objects, arrays,
|
|
64
|
+
* strings, booleans, null — no cycles, no class instances, no undefined, and
|
|
65
|
+
* within the depth/node limits. Structured clone can deliver Dates, Maps,
|
|
66
|
+
* typed arrays and cycles; none of those are allowed across the boundary.
|
|
67
|
+
*/
|
|
68
|
+
export declare function checkJsonValue(value: unknown, limits?: {
|
|
69
|
+
maxDepth?: number;
|
|
70
|
+
maxNodes?: number;
|
|
71
|
+
}): SchemaIssue | null;
|
|
72
|
+
/**
|
|
73
|
+
* Validates `value` against `schema`. Returns every issue found (up to
|
|
74
|
+
* `maxIssues`); an empty array means valid. Never throws, never loops forever.
|
|
75
|
+
*/
|
|
76
|
+
export declare function validateJson(schema: CardJsonSchema, value: unknown, options?: ValidateOptions): SchemaIssue[];
|
|
77
|
+
export interface SchemaCheckOptions {
|
|
78
|
+
/** Card-declared schemas: false (default). Only the host's own schemas use `pattern`. */
|
|
79
|
+
allowPattern?: boolean;
|
|
80
|
+
maxNodes?: number;
|
|
81
|
+
maxDepth?: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Checks that `schema` is a schema this engine supports, within limits.
|
|
85
|
+
* Used on every card-declared schema (ports, tools, settings are built from
|
|
86
|
+
* the manifest) before it is ever used to validate anything.
|
|
87
|
+
*/
|
|
88
|
+
export declare function checkCardSchema(schema: unknown, options?: SchemaCheckOptions): SchemaIssue[];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const CARD_LANGUAGES: readonly ["en", "ru", "zh"];
|
|
2
|
+
export type CardLanguage = (typeof CARD_LANGUAGES)[number];
|
|
3
|
+
/** A plain string, or `{ "en": "...", "ru": "...", "zh": "..." }` with `en` required. */
|
|
4
|
+
export type LocalizedText = string | ({
|
|
5
|
+
en: string;
|
|
6
|
+
} & Partial<Record<Exclude<CardLanguage, 'en'>, string>>);
|
|
7
|
+
/** The text for `language`, falling back to English. */
|
|
8
|
+
export declare function resolveLocalized(text: LocalizedText | undefined, language: CardLanguage): string;
|
|
9
|
+
/** Every language variant of a localized text (for length/emptiness checks). */
|
|
10
|
+
export declare function localizedVariants(text: LocalizedText): string[];
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { type CardJsonSchema } from './jsonSchema.js';
|
|
2
|
+
import { type LocalizedText } from './localized.js';
|
|
3
|
+
import { type ManifestPermission, type NormalizedPermission } from './permissions.js';
|
|
4
|
+
import { type PortDeclaration } from './ports.js';
|
|
5
|
+
export declare const MANIFEST_SCHEMA_ID = "https://neurosquad.ai/schemas/neurosquad-card.v1.json";
|
|
6
|
+
export declare const CARD_NAME_RE: RegExp;
|
|
7
|
+
export declare const TOOL_NAME_RE: RegExp;
|
|
8
|
+
export declare const SETTING_KEY_RE: RegExp;
|
|
9
|
+
export interface CardSize {
|
|
10
|
+
/** Flow units = CSS pixels at zoom 1. */
|
|
11
|
+
w: number;
|
|
12
|
+
h: number;
|
|
13
|
+
}
|
|
14
|
+
export declare const SIZE_BOUNDS: {
|
|
15
|
+
readonly minW: 200;
|
|
16
|
+
readonly minH: 120;
|
|
17
|
+
readonly maxW: 2400;
|
|
18
|
+
readonly maxH: 1800;
|
|
19
|
+
};
|
|
20
|
+
export declare const DEFAULT_CARD_SIZE: CardSize;
|
|
21
|
+
export declare const SETTING_TYPES: readonly ["string", "text", "number", "boolean", "select", "secret", "color"];
|
|
22
|
+
export type SettingType = (typeof SETTING_TYPES)[number];
|
|
23
|
+
export type SettingValue = string | number | boolean;
|
|
24
|
+
/** One field of the settings form the host renders (HeroUI) for the card. */
|
|
25
|
+
export interface SettingField {
|
|
26
|
+
key: string;
|
|
27
|
+
type: SettingType;
|
|
28
|
+
label: LocalizedText;
|
|
29
|
+
description?: LocalizedText;
|
|
30
|
+
/** Not allowed for `secret`. */
|
|
31
|
+
default?: SettingValue;
|
|
32
|
+
required?: boolean;
|
|
33
|
+
/** `instance` (default): per card on the canvas. `package`: shared by every card of this package. */
|
|
34
|
+
scope?: 'instance' | 'package';
|
|
35
|
+
placeholder?: LocalizedText;
|
|
36
|
+
/** string / text / secret. */
|
|
37
|
+
maxLength?: number;
|
|
38
|
+
/** number. */
|
|
39
|
+
min?: number;
|
|
40
|
+
max?: number;
|
|
41
|
+
step?: number;
|
|
42
|
+
/** select: 1–50 options. */
|
|
43
|
+
options?: {
|
|
44
|
+
value: string;
|
|
45
|
+
label: LocalizedText;
|
|
46
|
+
}[];
|
|
47
|
+
}
|
|
48
|
+
/** A tool connected agents get over MCP (spec §8.10). */
|
|
49
|
+
export interface ToolDeclaration {
|
|
50
|
+
/** `^[a-z][a-z0-9_]{0,39}$`; the agent sees `<card name with _>_<name>`. */
|
|
51
|
+
name: string;
|
|
52
|
+
title?: string;
|
|
53
|
+
/** English, for the model. Say what it does and what it returns. */
|
|
54
|
+
description: string;
|
|
55
|
+
/** A card schema with `type: "object"`. */
|
|
56
|
+
inputSchema: CardJsonSchema;
|
|
57
|
+
/** True when the tool changes nothing (shown to the agent as a hint). */
|
|
58
|
+
readOnly?: boolean;
|
|
59
|
+
/** Default 30 000, max 120 000. */
|
|
60
|
+
timeoutMs?: number;
|
|
61
|
+
}
|
|
62
|
+
export interface CardManifest {
|
|
63
|
+
$schema?: string;
|
|
64
|
+
manifestVersion: 1;
|
|
65
|
+
/** Package slug `^[a-z][a-z0-9-]{1,47}$` — tool prefix, custom port type namespace. */
|
|
66
|
+
name: string;
|
|
67
|
+
displayName: LocalizedText;
|
|
68
|
+
/** SemVer of the package (informational — installs are pinned to a commit). */
|
|
69
|
+
version: string;
|
|
70
|
+
description?: LocalizedText;
|
|
71
|
+
author?: {
|
|
72
|
+
name: string;
|
|
73
|
+
url?: string;
|
|
74
|
+
};
|
|
75
|
+
license?: string;
|
|
76
|
+
homepage?: string;
|
|
77
|
+
/** PNG or WebP inside the package, square, ≤ 128 KB. */
|
|
78
|
+
icon?: string;
|
|
79
|
+
/** CARD_PROTOCOL_VERSION the card was built against. */
|
|
80
|
+
protocol: number;
|
|
81
|
+
/** Lowest NeuroSquad version that can run it (SemVer). */
|
|
82
|
+
minAppVersion?: string;
|
|
83
|
+
/** HTML page loaded into the card frame. Default `index.html`. */
|
|
84
|
+
entry?: string;
|
|
85
|
+
card?: {
|
|
86
|
+
defaultSize?: CardSize;
|
|
87
|
+
minSize?: CardSize;
|
|
88
|
+
maxSize?: CardSize;
|
|
89
|
+
};
|
|
90
|
+
permissions?: ManifestPermission[];
|
|
91
|
+
settings?: SettingField[];
|
|
92
|
+
ports?: {
|
|
93
|
+
inputs?: PortDeclaration[];
|
|
94
|
+
outputs?: PortDeclaration[];
|
|
95
|
+
};
|
|
96
|
+
tools?: ToolDeclaration[];
|
|
97
|
+
keywords?: string[];
|
|
98
|
+
}
|
|
99
|
+
/** The manifest with defaults filled in and permissions canonicalized — what main stores and serves. */
|
|
100
|
+
export interface NormalizedManifest extends Omit<CardManifest, 'entry' | 'card' | 'permissions' | 'settings' | 'ports' | 'tools'> {
|
|
101
|
+
entry: string;
|
|
102
|
+
card: {
|
|
103
|
+
defaultSize: CardSize;
|
|
104
|
+
minSize: CardSize;
|
|
105
|
+
maxSize: CardSize;
|
|
106
|
+
};
|
|
107
|
+
permissions: NormalizedPermission[];
|
|
108
|
+
settings: SettingField[];
|
|
109
|
+
ports: {
|
|
110
|
+
inputs: PortDeclaration[];
|
|
111
|
+
outputs: PortDeclaration[];
|
|
112
|
+
};
|
|
113
|
+
tools: ToolDeclaration[];
|
|
114
|
+
}
|
|
115
|
+
export declare const MANIFEST_SCHEMA: CardJsonSchema;
|
|
116
|
+
export type ManifestIssueCode = 'json' | 'schema' | 'semantic' | 'protocol';
|
|
117
|
+
export interface ManifestIssue {
|
|
118
|
+
path: string;
|
|
119
|
+
message: string;
|
|
120
|
+
code: ManifestIssueCode;
|
|
121
|
+
}
|
|
122
|
+
export type ManifestResult = {
|
|
123
|
+
ok: true;
|
|
124
|
+
manifest: NormalizedManifest;
|
|
125
|
+
warnings: ManifestIssue[];
|
|
126
|
+
} | {
|
|
127
|
+
ok: false;
|
|
128
|
+
issues: ManifestIssue[];
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Every MCP tool name the app registers itself (main/mcp/server.ts and the
|
|
132
|
+
* plugin cards). A card tool may never be exposed under one of these: the MCP
|
|
133
|
+
* SDK refuses duplicates, and an agent (or a CLAUDE.md) that learned a
|
|
134
|
+
* built-in name must never reach community code with it (MC-6). A test in
|
|
135
|
+
* main scans the sources and fails when a new built-in is missing here.
|
|
136
|
+
*/
|
|
137
|
+
export declare const BUILTIN_MCP_TOOL_NAMES: readonly string[];
|
|
138
|
+
/** Is this display text free of control, bidi and invisible characters? */
|
|
139
|
+
export declare function isSafeDisplayText(text: string, multiline?: boolean): boolean;
|
|
140
|
+
/**
|
|
141
|
+
* Words a card from anyone but the official organization may not use in its
|
|
142
|
+
* name or author (L3): they would pass for the app itself or its badge.
|
|
143
|
+
* Main refuses them for non-official sources; `validate` warns.
|
|
144
|
+
*/
|
|
145
|
+
export declare function impersonationIssues(manifest: {
|
|
146
|
+
displayName: LocalizedText;
|
|
147
|
+
author?: {
|
|
148
|
+
name: string;
|
|
149
|
+
};
|
|
150
|
+
}): ManifestIssue[];
|
|
151
|
+
/** Parses the manifest file's text (size-checked) and validates it. */
|
|
152
|
+
export declare function parseManifestText(text: string, appVersion?: string): ManifestResult;
|
|
153
|
+
/**
|
|
154
|
+
* Validates a parsed manifest. `appVersion`, when given, is compared with
|
|
155
|
+
* `minAppVersion`. Never throws.
|
|
156
|
+
*/
|
|
157
|
+
export declare function validateManifest(raw: unknown, appVersion?: string): ManifestResult;
|
|
158
|
+
/** Default values of the settings form (secrets excluded). */
|
|
159
|
+
export declare function defaultSettings(fields: readonly SettingField[]): Record<string, SettingValue>;
|
|
160
|
+
/** Checks one settings patch against the fields; returns the problems (empty = ok). Secrets are rejected here — they go through their own channel. */
|
|
161
|
+
export declare function checkSettingValues(fields: readonly SettingField[], values: Record<string, unknown>): {
|
|
162
|
+
key: string;
|
|
163
|
+
message: string;
|
|
164
|
+
}[];
|
|
165
|
+
/** What an install/update consent dialog lists, in order (renderer localizes; CLI prints English). */
|
|
166
|
+
export type ConsentItem = {
|
|
167
|
+
kind: 'permission';
|
|
168
|
+
permission: NormalizedPermission;
|
|
169
|
+
} | {
|
|
170
|
+
kind: 'tools';
|
|
171
|
+
names: string[];
|
|
172
|
+
} | {
|
|
173
|
+
kind: 'ports';
|
|
174
|
+
inputs: number;
|
|
175
|
+
outputs: number;
|
|
176
|
+
};
|
|
177
|
+
export declare function consentItems(manifest: NormalizedManifest): ConsentItem[];
|
|
178
|
+
/** The MCP tool name an agent sees: `<name with - → _>_<tool>`, ≤ 64 chars (main dedupes collisions with `_2`, `_3`…). */
|
|
179
|
+
export declare function mcpToolName(packageName: string, tool: string): string;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A manifest host pattern: `api.example.com`, `api.example.com:8443` or
|
|
3
|
+
* `*.example.com` (any subdomain, not the apex). Lowercase, at least two
|
|
4
|
+
* labels after any wildcard, no IP literals, no `localhost` (that is the
|
|
5
|
+
* separate `network.local` permission), no bare `*`.
|
|
6
|
+
*/
|
|
7
|
+
export interface HostPattern {
|
|
8
|
+
wildcard: boolean;
|
|
9
|
+
/** Hostname without the `*.`. */
|
|
10
|
+
host: string;
|
|
11
|
+
/** Absent = 443. */
|
|
12
|
+
port?: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function parseHostPattern(pattern: string): {
|
|
15
|
+
ok: true;
|
|
16
|
+
value: HostPattern;
|
|
17
|
+
} | {
|
|
18
|
+
ok: false;
|
|
19
|
+
error: string;
|
|
20
|
+
};
|
|
21
|
+
/** Canonical text of a pattern (what grants store and consent shows). */
|
|
22
|
+
export declare function formatHostPattern(pattern: HostPattern): string;
|
|
23
|
+
/** Does `hostname:port` (from a parsed https URL, port defaulting to 443) match the pattern? */
|
|
24
|
+
export declare function hostMatchesPattern(pattern: HostPattern, hostname: string, port?: number): boolean;
|
|
25
|
+
export declare function hostAllowed(patterns: readonly string[], hostname: string, port?: number): boolean;
|
|
26
|
+
/** Hostnames `network.local` covers. */
|
|
27
|
+
export declare function isLocalHostname(hostname: string): boolean;
|
|
28
|
+
/**
|
|
29
|
+
* True for any address a card must never reach through a *public* host
|
|
30
|
+
* grant: loopback, private, link-local (incl. cloud metadata 169.254.169.254),
|
|
31
|
+
* CGNAT, multicast, reserved, and their IPv4-mapped / NAT64 IPv6 forms.
|
|
32
|
+
* Main applies it to every resolved address of every hop (DNS rebinding).
|
|
33
|
+
* Unparseable input counts as blocked.
|
|
34
|
+
*/
|
|
35
|
+
export declare function isBlockedAddress(ip: string): boolean;
|
|
36
|
+
/** Request headers a card may not set (lowercase). The proxy sets its own `user-agent`. */
|
|
37
|
+
export declare const FORBIDDEN_REQUEST_HEADERS: ReadonlySet<string>;
|
|
38
|
+
/** Response headers never passed back to a card (lowercase). */
|
|
39
|
+
export declare const HIDDEN_RESPONSE_HEADERS: ReadonlySet<string>;
|
|
40
|
+
export declare function checkRequestHeader(name: string, value: string): string | null;
|
|
41
|
+
/**
|
|
42
|
+
* `{{secret:<settingKey>}}` in a *header value* is replaced by main with the
|
|
43
|
+
* card's secret setting of that key — the card itself never sees the secret.
|
|
44
|
+
* Only in headers (not URL or body, which end up in server logs and
|
|
45
|
+
* redirects), and only on requests to hosts the card was granted.
|
|
46
|
+
*/
|
|
47
|
+
export declare const SECRET_PLACEHOLDER_RE: RegExp;
|
|
48
|
+
export declare function secretPlaceholders(value: string): string[];
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { LocalizedText } from './localized.js';
|
|
2
|
+
export declare const PERMISSION_IDS: readonly ["agents.read", "agents.output", "agents.prompt", "terminals.write", "cards.connected", "network", "network.local", "fs.read", "fs.write", "clipboard.write", "canvas.spawn", "background", "usage.read"];
|
|
3
|
+
export type PermissionId = (typeof PERMISSION_IDS)[number];
|
|
4
|
+
export declare function isPermissionId(value: unknown): value is PermissionId;
|
|
5
|
+
export type PermissionRisk = 'low' | 'medium' | 'high';
|
|
6
|
+
export interface PermissionInfo {
|
|
7
|
+
risk: PermissionRisk;
|
|
8
|
+
/** Granting this one grants these too (consent lists only the strongest). */
|
|
9
|
+
implies: readonly PermissionId[];
|
|
10
|
+
/** Must carry a non-empty `hosts` list in the manifest. */
|
|
11
|
+
needsHosts: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare const PERMISSION_INFO: Record<PermissionId, PermissionInfo>;
|
|
14
|
+
/**
|
|
15
|
+
* Canonical English consent text. The desktop UI shows the same meaning
|
|
16
|
+
* through i18n keys `ext.cardSdk.permissions.<id with dots → camel>.title|body`
|
|
17
|
+
* in en/ru/zh (these strings are the en source); the SDK CLI prints these.
|
|
18
|
+
* `{{hosts}}` is replaced with the comma-separated host list.
|
|
19
|
+
*/
|
|
20
|
+
export declare const PERMISSION_TEXT_EN: Record<PermissionId, {
|
|
21
|
+
title: string;
|
|
22
|
+
body: string;
|
|
23
|
+
}>;
|
|
24
|
+
/** i18n key stem for a permission: `agents.prompt` → `agentsPrompt`. */
|
|
25
|
+
export declare function permissionI18nStem(id: PermissionId): string;
|
|
26
|
+
/** One entry of the manifest's `permissions` list. */
|
|
27
|
+
export type ManifestPermission = PermissionId | {
|
|
28
|
+
id: PermissionId;
|
|
29
|
+
/** `network` only: host patterns (network.ts). */
|
|
30
|
+
hosts?: string[];
|
|
31
|
+
/** Why the card needs it — shown under the consent line. */
|
|
32
|
+
reason?: LocalizedText;
|
|
33
|
+
/** Not asked at install; the card asks at runtime with permissions.request(). */
|
|
34
|
+
optional?: boolean;
|
|
35
|
+
};
|
|
36
|
+
export interface NormalizedPermission {
|
|
37
|
+
id: PermissionId;
|
|
38
|
+
/** Canonical, sorted, deduplicated. Present only for `network`. */
|
|
39
|
+
hosts?: string[];
|
|
40
|
+
reason?: LocalizedText;
|
|
41
|
+
optional: boolean;
|
|
42
|
+
}
|
|
43
|
+
export interface PermissionIssue {
|
|
44
|
+
index: number;
|
|
45
|
+
message: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Canonical form of a manifest's permission list: one entry per id (a
|
|
49
|
+
* duplicate `network` merges its hosts; required wins over optional),
|
|
50
|
+
* hosts parsed and canonicalized, sorted by PERMISSION_IDS order.
|
|
51
|
+
*/
|
|
52
|
+
export declare function normalizePermissions(entries: readonly ManifestPermission[]): {
|
|
53
|
+
permissions: NormalizedPermission[];
|
|
54
|
+
issues: PermissionIssue[];
|
|
55
|
+
};
|
|
56
|
+
/** The ids a set of granted ids effectively allows (with implications). */
|
|
57
|
+
export declare function expandGranted(ids: Iterable<PermissionId>): Set<PermissionId>;
|
|
58
|
+
/** What the user has granted to a package (stored by main in the package registry). */
|
|
59
|
+
export interface PermissionGrant {
|
|
60
|
+
id: PermissionId;
|
|
61
|
+
/** `network`: exactly the hosts consented to. */
|
|
62
|
+
hosts?: string[];
|
|
63
|
+
grantedAt: string;
|
|
64
|
+
}
|
|
65
|
+
export interface PermissionDiff {
|
|
66
|
+
/** Required permissions new in `after` (never granted before) — need consent. */
|
|
67
|
+
added: NormalizedPermission[];
|
|
68
|
+
/** Optional permissions new in `after` — shown, asked later at runtime. */
|
|
69
|
+
addedOptional: NormalizedPermission[];
|
|
70
|
+
/** Gone in `after` — their grants are dropped. */
|
|
71
|
+
removed: PermissionId[];
|
|
72
|
+
/** `network` hosts in `after` that were not granted — need consent. */
|
|
73
|
+
addedHosts: string[];
|
|
74
|
+
removedHosts: string[];
|
|
75
|
+
/**
|
|
76
|
+
* Non-permission changes that also need consent (MC-7): new or reworded MCP
|
|
77
|
+
* tools, new or retyped ports, new secret settings, a changed name/author/
|
|
78
|
+
* homepage. Present when main compared against what was consented before.
|
|
79
|
+
*/
|
|
80
|
+
surface?: SurfaceDiff;
|
|
81
|
+
/** Anything above that requires the user to consent before the update applies. */
|
|
82
|
+
needsConsent: boolean;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* What a user saw and accepted besides permissions: the MCP tools (their text
|
|
86
|
+
* goes into every connected agent's context), the ports (what arrows carry),
|
|
87
|
+
* the secret settings, and who the card says it is. Main stores it on the
|
|
88
|
+
* package record at every consent and diffs updates and dev edits against it.
|
|
89
|
+
*/
|
|
90
|
+
export interface ConsentSurface {
|
|
91
|
+
displayName: string;
|
|
92
|
+
author: string | null;
|
|
93
|
+
homepage: string | null;
|
|
94
|
+
tools: {
|
|
95
|
+
name: string;
|
|
96
|
+
description: string;
|
|
97
|
+
}[];
|
|
98
|
+
inputs: {
|
|
99
|
+
id: string;
|
|
100
|
+
type: string;
|
|
101
|
+
}[];
|
|
102
|
+
outputs: {
|
|
103
|
+
id: string;
|
|
104
|
+
type: string;
|
|
105
|
+
}[];
|
|
106
|
+
secrets: string[];
|
|
107
|
+
}
|
|
108
|
+
export interface SurfaceDiff {
|
|
109
|
+
/** New tools, and tools whose description changed (`previous` set). */
|
|
110
|
+
tools: {
|
|
111
|
+
name: string;
|
|
112
|
+
description: string;
|
|
113
|
+
previous?: string;
|
|
114
|
+
}[];
|
|
115
|
+
/** New ports, and ports whose type changed (`previousType` set). */
|
|
116
|
+
inputs: {
|
|
117
|
+
id: string;
|
|
118
|
+
type: string;
|
|
119
|
+
previousType?: string;
|
|
120
|
+
}[];
|
|
121
|
+
outputs: {
|
|
122
|
+
id: string;
|
|
123
|
+
type: string;
|
|
124
|
+
previousType?: string;
|
|
125
|
+
}[];
|
|
126
|
+
secrets: string[];
|
|
127
|
+
identity: {
|
|
128
|
+
field: 'displayName' | 'author' | 'homepage';
|
|
129
|
+
from: string | null;
|
|
130
|
+
to: string | null;
|
|
131
|
+
}[];
|
|
132
|
+
needsConsent: boolean;
|
|
133
|
+
}
|
|
134
|
+
/** The fields of a normalized manifest `consentSurface` reads (structural, so no import cycle). */
|
|
135
|
+
export interface ConsentSurfaceSource {
|
|
136
|
+
displayName: LocalizedText;
|
|
137
|
+
author?: {
|
|
138
|
+
name: string;
|
|
139
|
+
};
|
|
140
|
+
homepage?: string;
|
|
141
|
+
tools: readonly {
|
|
142
|
+
name: string;
|
|
143
|
+
description: string;
|
|
144
|
+
}[];
|
|
145
|
+
ports: {
|
|
146
|
+
inputs: readonly {
|
|
147
|
+
id: string;
|
|
148
|
+
type: string;
|
|
149
|
+
}[];
|
|
150
|
+
outputs: readonly {
|
|
151
|
+
id: string;
|
|
152
|
+
type: string;
|
|
153
|
+
}[];
|
|
154
|
+
};
|
|
155
|
+
settings: readonly {
|
|
156
|
+
key: string;
|
|
157
|
+
type: string;
|
|
158
|
+
}[];
|
|
159
|
+
}
|
|
160
|
+
export declare function consentSurface(manifest: ConsentSurfaceSource): ConsentSurface;
|
|
161
|
+
/** What changed from the consented surface to `after`; removals never need consent. */
|
|
162
|
+
export declare function diffSurface(before: ConsentSurface, after: ConsentSurface): SurfaceDiff;
|
|
163
|
+
/**
|
|
164
|
+
* The full re-consent check main runs on updates, reinstalls and dev edits:
|
|
165
|
+
* the permission diff plus, when a consented surface is known, the surface diff.
|
|
166
|
+
*/
|
|
167
|
+
export declare function diffForConsent(granted: readonly PermissionGrant[], consented: ConsentSurface | undefined, after: readonly NormalizedPermission[], afterManifest: ConsentSurfaceSource): PermissionDiff;
|
|
168
|
+
/**
|
|
169
|
+
* Compares a new manifest's permissions with what is granted now (spec §10.5:
|
|
170
|
+
* permission escalation on update → re-consent). `granted` is the grant
|
|
171
|
+
* list, `after` the new manifest's normalized permissions.
|
|
172
|
+
*/
|
|
173
|
+
export declare function diffPermissions(granted: readonly PermissionGrant[], after: readonly NormalizedPermission[]): PermissionDiff;
|
|
174
|
+
/**
|
|
175
|
+
* The grant list after the user consents to `after` (install, or an update
|
|
176
|
+
* with its diff approved): keeps still-declared grants (hosts narrowed to
|
|
177
|
+
* what is still declared), adds every required permission, keeps optional
|
|
178
|
+
* ones only if they were granted before.
|
|
179
|
+
*/
|
|
180
|
+
export declare function grantsAfterConsent(granted: readonly PermissionGrant[], after: readonly NormalizedPermission[], now: string): PermissionGrant[];
|