@holokai/harbor-sdk 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -0
- package/dist/chunks/chunk-G4KJ2OCU.js +319 -0
- package/dist/contract.d.ts +613 -0
- package/dist/contract.js +51 -0
- package/dist/holokai-logo.d.ts +5 -0
- package/dist/index.d.ts +104 -0
- package/dist/index.js +212 -0
- package/dist/manifest.d.ts +26 -0
- package/dist/manifest.js +1 -0
- package/dist/package.d.ts +79 -0
- package/dist/package.js +131 -0
- package/dist/page-reader.d.ts +53 -0
- package/dist/runtime.d.ts +220 -0
- package/dist/runtime.js +56 -0
- package/dist/sign-in-page.d.ts +5 -0
- package/dist/sign-in-page.js +96 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# @holokai/harbor-sdk
|
|
2
|
+
|
|
3
|
+
The harbor extension to [MCP Apps](https://github.com/modelcontextprotocol/ext-apps): what a hosted
|
|
4
|
+
view can ask harbor for beyond the MCP Apps spec, spelled once.
|
|
5
|
+
|
|
6
|
+
Two entry points:
|
|
7
|
+
|
|
8
|
+
- `@holokai/harbor-sdk/contract` — the public wire. Zero imports. Method names, params and result
|
|
9
|
+
types, a JSON Schema per method's params, the event map, the grant map, and the shape harbor
|
|
10
|
+
advertises under `hostCapabilities.experimental['ai.holokai/harbor']`. Harbor's own gate imports
|
|
11
|
+
this module for validation, so the SDK's types cannot drift from what the gate enforces.
|
|
12
|
+
- `@holokai/harbor-sdk` — `connectHarbor(app)`: a thin layer OVER the ext-apps `App` instance
|
|
13
|
+
(never a second JSON-RPC client on the same channel). Returns `null` in any host that is not
|
|
14
|
+
harbor, so the same app runs unchanged in Claude, ChatGPT, VS Code or Goose as a plain MCP App.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import {App} from '@modelcontextprotocol/ext-apps';
|
|
18
|
+
import {connectHarbor} from '@holokai/harbor-sdk';
|
|
19
|
+
|
|
20
|
+
const app = new App({name: 'my-app', version: '1.0.0'});
|
|
21
|
+
await app.connect();
|
|
22
|
+
const harbor = connectHarbor(app);
|
|
23
|
+
if (harbor) {
|
|
24
|
+
const {workflows} = await harbor.workflows.list({limit: 20});
|
|
25
|
+
const off = harbor.events.on('run.changed', ({workflow}) => console.log(workflow.status));
|
|
26
|
+
}
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Opening a file: `harbor.files.open({name, tool, arguments})` asks harbor to open a file the app
|
|
30
|
+
identifies. Harbor calls `tool` on the app's own server; the tool answers a `CallToolResult` whose
|
|
31
|
+
content holds exactly one `{type: 'resource', resource: {uri, mimeType?, blob}}` carrying the bytes
|
|
32
|
+
base64. Harbor keeps a read-only copy named `name` and opens it in its own editor; the answer is
|
|
33
|
+
`{name}`. Needs the `files.open` grant.
|
|
34
|
+
|
|
35
|
+
Opening a link: `harbor.browser.open({url})` asks harbor to open a URL in the person's own browser.
|
|
36
|
+
A view's frame is sandboxed without `allow-popups`, so a link of its own goes nowhere; this is the
|
|
37
|
+
way out. **https only** — harbor parses the URL in its main process and refuses every other scheme
|
|
38
|
+
with `-32602`, so `http:`, `file:`, `javascript:` and an application's own scheme are all dead
|
|
39
|
+
regardless of what the page checks. The answer is `{url}` as harbor parsed it. Needs the
|
|
40
|
+
`browser.open` grant.
|
|
41
|
+
|
|
42
|
+
Saying what the page is showing: `reportView(app, {state, names, text})` sends one WHOLE snapshot of
|
|
43
|
+
the view on the spec's own `ui/update-model-context`, and `savedView(app)` answers what harbor saved
|
|
44
|
+
for this page last time. Neither goes through `connectHarbor`: `reportView` works under any MCP Apps
|
|
45
|
+
host, and `savedView` answers `null` under every host but harbor. Call both after `app.connect()`.
|
|
46
|
+
|
|
47
|
+
- **The advertisement is the disclosure.** Harbor answers `ui/initialize` with
|
|
48
|
+
`hostCapabilities.updateModelContext` naming the modalities it takes, and `reportView` sends only
|
|
49
|
+
those. A host that advertises no `structuredContent` is sent the text block and neither harbor
|
|
50
|
+
key, so a pane width and a subject line never enter a foreign host's context; a host that
|
|
51
|
+
advertises nothing is sent nothing at all.
|
|
52
|
+
- **`view.state`** admits the structured view — `{place, item, filter, query, panes}`, harbor's key
|
|
53
|
+
under `structuredContent`. Harbor remembers it and hands it back; it goes nowhere else.
|
|
54
|
+
- **`view.content`** admits what the page says in its own words (the `content` blocks) and the two
|
|
55
|
+
names it offers for what it has open (`{item, place}`, its own key under `structuredContent`).
|
|
56
|
+
Harbor holds these only while the view is mounted.
|
|
57
|
+
- **Refused:** a `content` block that is not text — harbor advertises no other modality, so an
|
|
58
|
+
image, audio, a resource or a resource link is a `-32602` rather than a dropped block. So is a
|
|
59
|
+
structured half that is present but malformed: an unknown field, a string over 256 characters, a
|
|
60
|
+
`panes` bag over 8 entries, a pane name that is not a short identifier, or a pane width that is
|
|
61
|
+
not a finite number. **Not refused, simply not kept:** a half that is absent or ungranted, a key
|
|
62
|
+
belonging to another extension, a name over 120 characters or empty once its control characters
|
|
63
|
+
are gone, and text over the result budget — one unusable half never costs the report the other.
|
|
64
|
+
- **The saved view arrives on the `ui/initialize` ANSWER**, under harbor's key in `hostContext`, so
|
|
65
|
+
a page reads it before its first frame. Harbor never pushes it again.
|
|
66
|
+
|
|
67
|
+
What harbor does with each half, and why the two never cross, is `docs/design/hosted-apps.md`,
|
|
68
|
+
section 9.
|
|
69
|
+
|
|
70
|
+
Versioning: the extension object carries `version`; methods are additive within a major. Events are
|
|
71
|
+
invalidation signals: a subscription's `seq` increments per delivered event, and a gap means "re-read
|
|
72
|
+
a snapshot" — there is no replay.
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import {createRequire as __harborCreateRequire} from 'node:module'; const require = __harborCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/contract.ts
|
|
4
|
+
var HARBOR_EXTENSION_ID = "ai.holokai/harbor";
|
|
5
|
+
var HARBOR_PAGE_READER = { tool: "read_page", reads: "page" };
|
|
6
|
+
var HARBOR_EXTENSION_VERSION = "0.2";
|
|
7
|
+
var MCP_APPS_PROTOCOL_VERSION = "2026-01-26";
|
|
8
|
+
var HARBOR_METHODS = {
|
|
9
|
+
workflowsList: "harbor/workflows/list",
|
|
10
|
+
workflowsSubmit: "harbor/workflows/submit",
|
|
11
|
+
workflowsGet: "harbor/workflows/get",
|
|
12
|
+
capabilitiesList: "harbor/capabilities/list",
|
|
13
|
+
eventsSubscribe: "harbor/events/subscribe",
|
|
14
|
+
eventsUnsubscribe: "harbor/events/unsubscribe",
|
|
15
|
+
filesOpen: "harbor/files/open",
|
|
16
|
+
filesAdd: "harbor/files/add",
|
|
17
|
+
filesLink: "harbor/files/link",
|
|
18
|
+
browserOpen: "harbor/browser/open",
|
|
19
|
+
barSet: "harbor/bar/set",
|
|
20
|
+
viewsOpen: "harbor/views/open",
|
|
21
|
+
viewStatus: "harbor/view/status"
|
|
22
|
+
};
|
|
23
|
+
var HARBOR_EVENT_NOTIFICATION = "harbor/notifications/event";
|
|
24
|
+
var HARBOR_BAR_NOTIFICATION = "harbor/notifications/bar";
|
|
25
|
+
var WORKFLOWS_LIST_LIMIT = { min: 1, max: 50, default: 20 };
|
|
26
|
+
var WORKFLOW_TASKS_MAX = 50;
|
|
27
|
+
var BAR_LIMITS = {
|
|
28
|
+
placeholderChars: 60,
|
|
29
|
+
labelChars: 60,
|
|
30
|
+
valueChars: 500,
|
|
31
|
+
nameChars: 80,
|
|
32
|
+
items: 8,
|
|
33
|
+
itemLabelChars: 60,
|
|
34
|
+
itemIdChars: 40
|
|
35
|
+
};
|
|
36
|
+
var VIEW_STATUSES = ["idle", "running", "waiting", "completed", "failed"];
|
|
37
|
+
var HARBOR_VIEW_STATE_LIMITS = {
|
|
38
|
+
/** Longest a `place`, `item`, `filter` or `query` may be. */
|
|
39
|
+
valueChars: 256,
|
|
40
|
+
/** Most entries `panes` may carry. */
|
|
41
|
+
paneEntries: 8,
|
|
42
|
+
/** Longest a pane name may be. */
|
|
43
|
+
paneNameChars: 32
|
|
44
|
+
};
|
|
45
|
+
var PANE_NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9_-]*$/;
|
|
46
|
+
var HARBOR_VIEW_STATE_STRING_KEYS = ["place", "item", "filter", "query"];
|
|
47
|
+
function validateViewState(value) {
|
|
48
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
49
|
+
return { ok: false, message: "the view state must be an object" };
|
|
50
|
+
}
|
|
51
|
+
const record = value;
|
|
52
|
+
const state = {};
|
|
53
|
+
for (const key of Object.keys(record)) {
|
|
54
|
+
const known = key === "panes" || HARBOR_VIEW_STATE_STRING_KEYS.includes(key);
|
|
55
|
+
if (!known) return { ok: false, message: `field '${key}' is not part of the view state` };
|
|
56
|
+
}
|
|
57
|
+
for (const key of HARBOR_VIEW_STATE_STRING_KEYS) {
|
|
58
|
+
const field = record[key];
|
|
59
|
+
if (field === void 0) continue;
|
|
60
|
+
if (typeof field !== "string") return { ok: false, message: `field '${key}' must be a string` };
|
|
61
|
+
if (field.length > HARBOR_VIEW_STATE_LIMITS.valueChars) {
|
|
62
|
+
return { ok: false, message: `field '${key}' is longer than ${HARBOR_VIEW_STATE_LIMITS.valueChars} chars` };
|
|
63
|
+
}
|
|
64
|
+
state[key] = field;
|
|
65
|
+
}
|
|
66
|
+
const panes = record["panes"];
|
|
67
|
+
if (panes !== void 0) {
|
|
68
|
+
if (typeof panes !== "object" || panes === null || Array.isArray(panes)) {
|
|
69
|
+
return { ok: false, message: "panes must be an object" };
|
|
70
|
+
}
|
|
71
|
+
const entries = Object.entries(panes);
|
|
72
|
+
if (entries.length > HARBOR_VIEW_STATE_LIMITS.paneEntries) {
|
|
73
|
+
return { ok: false, message: `panes takes at most ${HARBOR_VIEW_STATE_LIMITS.paneEntries} entries` };
|
|
74
|
+
}
|
|
75
|
+
const kept = {};
|
|
76
|
+
for (const [name, width] of entries) {
|
|
77
|
+
if (name.length > HARBOR_VIEW_STATE_LIMITS.paneNameChars || !PANE_NAME_PATTERN.test(name)) {
|
|
78
|
+
return { ok: false, message: "a pane name must be a short identifier" };
|
|
79
|
+
}
|
|
80
|
+
if (typeof width !== "number" || !Number.isFinite(width)) {
|
|
81
|
+
return { ok: false, message: "a pane width must be a finite number" };
|
|
82
|
+
}
|
|
83
|
+
kept[name] = width;
|
|
84
|
+
}
|
|
85
|
+
state.panes = kept;
|
|
86
|
+
}
|
|
87
|
+
return { ok: true, value: state };
|
|
88
|
+
}
|
|
89
|
+
var HARBOR_VIEW_NAMES_ID = "ai.holokai/harbor/names";
|
|
90
|
+
var HARBOR_VIEW_NAME_LIMITS = {
|
|
91
|
+
/** Longest an `item`, `place` or step name may be; a longer one is not kept. */
|
|
92
|
+
nameChars: 120,
|
|
93
|
+
/** Most steps a trail may hold; past it, the first step and the innermost ones are kept. */
|
|
94
|
+
trailSteps: 12,
|
|
95
|
+
/** Longest a step's id may be; a step with a longer one, or one holding a control character, is not kept. */
|
|
96
|
+
stepIdChars: 256
|
|
97
|
+
};
|
|
98
|
+
var VIEW_NAME_KEYS = ["item", "place"];
|
|
99
|
+
var VIEW_NAMES_FIELDS = [...VIEW_NAME_KEYS, "trail"];
|
|
100
|
+
var CONTROL_CHARS = /[\u0000-\u001f\u007f-\u009f]/g;
|
|
101
|
+
function validateViewNames(value) {
|
|
102
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
103
|
+
return { ok: false, message: "the view names must be an object" };
|
|
104
|
+
}
|
|
105
|
+
const record = value;
|
|
106
|
+
const names = {};
|
|
107
|
+
for (const key of Object.keys(record)) {
|
|
108
|
+
if (!VIEW_NAMES_FIELDS.includes(key)) {
|
|
109
|
+
return { ok: false, message: `field '${key}' is not part of the view names` };
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
for (const key of VIEW_NAME_KEYS) {
|
|
113
|
+
const field = record[key];
|
|
114
|
+
if (field === void 0) continue;
|
|
115
|
+
if (typeof field !== "string") return { ok: false, message: `field '${key}' must be a string` };
|
|
116
|
+
const clean = cleanName(field);
|
|
117
|
+
if (clean !== null) names[key] = clean;
|
|
118
|
+
}
|
|
119
|
+
const trail = record["trail"];
|
|
120
|
+
if (trail !== void 0) {
|
|
121
|
+
if (!Array.isArray(trail)) return { ok: false, message: `field 'trail' must be an array` };
|
|
122
|
+
const steps = [];
|
|
123
|
+
const cap = HARBOR_VIEW_NAME_LIMITS.trailSteps;
|
|
124
|
+
const kept = trail.length <= cap ? trail : [trail[0], ...trail.slice(trail.length - (cap - 1))];
|
|
125
|
+
for (const step of kept) {
|
|
126
|
+
if (typeof step !== "object" || step === null || Array.isArray(step)) {
|
|
127
|
+
return { ok: false, message: `a trail step must be an object` };
|
|
128
|
+
}
|
|
129
|
+
const { id, name } = step;
|
|
130
|
+
if (typeof id !== "string" || typeof name !== "string") {
|
|
131
|
+
return { ok: false, message: `a trail step must have a string 'id' and 'name'` };
|
|
132
|
+
}
|
|
133
|
+
const cleanStepName = cleanName(name);
|
|
134
|
+
if (id.length === 0 || id.length > HARBOR_VIEW_NAME_LIMITS.stepIdChars || id.replace(CONTROL_CHARS, "") !== id || cleanStepName === null) {
|
|
135
|
+
continue;
|
|
136
|
+
}
|
|
137
|
+
steps.push({ id, name: cleanStepName });
|
|
138
|
+
}
|
|
139
|
+
if (steps.length > 0) names.trail = steps;
|
|
140
|
+
}
|
|
141
|
+
return { ok: true, value: names };
|
|
142
|
+
}
|
|
143
|
+
function cleanName(field) {
|
|
144
|
+
const clean = field.replace(CONTROL_CHARS, " ").trim();
|
|
145
|
+
return clean.length === 0 || clean.length > HARBOR_VIEW_NAME_LIMITS.nameChars ? null : clean;
|
|
146
|
+
}
|
|
147
|
+
var UPDATE_MODEL_CONTEXT_PARAMS_SCHEMA = {
|
|
148
|
+
type: "object",
|
|
149
|
+
properties: {
|
|
150
|
+
content: { type: "array", description: "Context blocks for the model; harbor takes text blocks only." },
|
|
151
|
+
structuredContent: { type: "object", description: "Machine-readable context, keyed by extension id." },
|
|
152
|
+
_meta: { type: "object" }
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
var HARBOR_EVENT_NAMES = [
|
|
156
|
+
"harbor.heartbeat",
|
|
157
|
+
"run.changed",
|
|
158
|
+
"workflow.task",
|
|
159
|
+
"workflow.changed",
|
|
160
|
+
"server.notification"
|
|
161
|
+
];
|
|
162
|
+
var HARBOR_METHOD_PARAMS_SCHEMA = {
|
|
163
|
+
"harbor/workflows/list": {
|
|
164
|
+
type: "object",
|
|
165
|
+
properties: { limit: { type: "integer", description: "How many, newest first. 1..50, default 20." } }
|
|
166
|
+
},
|
|
167
|
+
"harbor/workflows/submit": {
|
|
168
|
+
type: "object",
|
|
169
|
+
properties: {
|
|
170
|
+
name: { type: "string", description: "A human label for the run; the server assigns the id." },
|
|
171
|
+
description: { type: "string" },
|
|
172
|
+
tasks: { type: "array", description: "The DAG, as WorkflowTaskSpec rows; validated in depth by harbor." }
|
|
173
|
+
},
|
|
174
|
+
required: ["name", "tasks"]
|
|
175
|
+
},
|
|
176
|
+
"harbor/workflows/get": {
|
|
177
|
+
type: "object",
|
|
178
|
+
properties: { workflowId: { type: "string" } },
|
|
179
|
+
required: ["workflowId"]
|
|
180
|
+
},
|
|
181
|
+
// No params: an empty properties map means any key is refused.
|
|
182
|
+
"harbor/capabilities/list": { type: "object", properties: {} },
|
|
183
|
+
"harbor/events/subscribe": {
|
|
184
|
+
type: "object",
|
|
185
|
+
properties: { events: { type: "array", description: "Event names to receive; each must be a known name." } },
|
|
186
|
+
required: ["events"]
|
|
187
|
+
},
|
|
188
|
+
"harbor/events/unsubscribe": {
|
|
189
|
+
type: "object",
|
|
190
|
+
properties: { subscriptionId: { type: "string" } },
|
|
191
|
+
required: ["subscriptionId"]
|
|
192
|
+
},
|
|
193
|
+
"harbor/files/open": {
|
|
194
|
+
type: "object",
|
|
195
|
+
properties: {
|
|
196
|
+
name: { type: "string", description: "The file name to open the copy under; a bare name, no folders." },
|
|
197
|
+
tool: { type: "string", description: "The app's own tool that returns the file's bytes." },
|
|
198
|
+
arguments: { type: "object", description: "Arguments for that tool." }
|
|
199
|
+
},
|
|
200
|
+
required: ["name", "tool"]
|
|
201
|
+
},
|
|
202
|
+
"harbor/files/add": {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
name: { type: "string", description: "The file name to save it under; a bare name, no folders." },
|
|
206
|
+
tool: { type: "string", description: "The app's own tool that returns the file's bytes." },
|
|
207
|
+
arguments: { type: "object", description: "Arguments for that tool." }
|
|
208
|
+
},
|
|
209
|
+
required: ["name", "tool"]
|
|
210
|
+
},
|
|
211
|
+
"harbor/files/link": {
|
|
212
|
+
type: "object",
|
|
213
|
+
properties: {
|
|
214
|
+
id: { type: "string", description: "A folder or file id this app's own tools gave." },
|
|
215
|
+
url: { type: "string", description: "The folder's address on its provider's own site." }
|
|
216
|
+
},
|
|
217
|
+
required: ["id"]
|
|
218
|
+
},
|
|
219
|
+
"harbor/browser/open": {
|
|
220
|
+
type: "object",
|
|
221
|
+
// `_meta` is the one key MCP allows on ANY request's params (a progress
|
|
222
|
+
// token rides it), and the validator refuses what a schema does not
|
|
223
|
+
// name — so a client that attaches one would be told its field is not
|
|
224
|
+
// allowed. `tools/call` names it for the same reason.
|
|
225
|
+
properties: {
|
|
226
|
+
url: { type: "string", description: "An https URL to open in the person\u2019s own browser." },
|
|
227
|
+
_meta: { type: "object" }
|
|
228
|
+
},
|
|
229
|
+
required: ["url"]
|
|
230
|
+
},
|
|
231
|
+
"harbor/bar/set": {
|
|
232
|
+
type: "object",
|
|
233
|
+
properties: {
|
|
234
|
+
search: {
|
|
235
|
+
type: "object",
|
|
236
|
+
description: "The search field, or null for none; its shape and caps are checked by harbor."
|
|
237
|
+
},
|
|
238
|
+
account: {
|
|
239
|
+
type: "object",
|
|
240
|
+
description: "The account menu, or null for none; its shape and caps are checked by harbor."
|
|
241
|
+
},
|
|
242
|
+
_meta: { type: "object" }
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
"harbor/views/open": {
|
|
246
|
+
type: "object",
|
|
247
|
+
properties: {
|
|
248
|
+
title: { type: "string", description: "What the new window is called until its page names what it shows." },
|
|
249
|
+
view: { type: "object", description: "The saved view the new page boots with; a HarborViewState." },
|
|
250
|
+
_meta: { type: "object" }
|
|
251
|
+
},
|
|
252
|
+
required: ["title", "view"]
|
|
253
|
+
},
|
|
254
|
+
"harbor/view/status": {
|
|
255
|
+
type: "object",
|
|
256
|
+
properties: {
|
|
257
|
+
status: {
|
|
258
|
+
type: "string",
|
|
259
|
+
enum: VIEW_STATUSES,
|
|
260
|
+
description: "What the page's tray button shows: idle, running, waiting, completed or failed."
|
|
261
|
+
},
|
|
262
|
+
_meta: { type: "object" }
|
|
263
|
+
},
|
|
264
|
+
required: ["status"]
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
var HARBOR_GRANT_OF = {
|
|
268
|
+
"harbor/workflows/list": "workflows.read",
|
|
269
|
+
"harbor/workflows/submit": "workflows.submit",
|
|
270
|
+
"harbor/workflows/get": "workflows.submit",
|
|
271
|
+
"harbor/capabilities/list": "capabilities.read",
|
|
272
|
+
"harbor/events/subscribe": "by-params",
|
|
273
|
+
"harbor/events/unsubscribe": "by-params",
|
|
274
|
+
"harbor/files/open": "files.open",
|
|
275
|
+
"harbor/files/add": "files.add",
|
|
276
|
+
"harbor/files/link": "files.add",
|
|
277
|
+
"harbor/browser/open": "browser.open",
|
|
278
|
+
"harbor/bar/set": "bar.set",
|
|
279
|
+
"harbor/views/open": "views.open",
|
|
280
|
+
"harbor/view/status": "view.state",
|
|
281
|
+
"harbor.heartbeat": "events.diagnostic",
|
|
282
|
+
"run.changed": "events.runs",
|
|
283
|
+
"workflow.task": "workflows.submit",
|
|
284
|
+
"workflow.changed": "workflows.submit",
|
|
285
|
+
"server.notification": "server.tools"
|
|
286
|
+
};
|
|
287
|
+
var STANDARD_METHOD_GRANT_OF = {
|
|
288
|
+
"tools/call": "server.tools",
|
|
289
|
+
"resources/list": "server.resources",
|
|
290
|
+
"resources/read": "server.resources",
|
|
291
|
+
"ui/update-model-context": ["view.state", "view.content"]
|
|
292
|
+
};
|
|
293
|
+
var WORKFLOW_TITLE_GRANT = "workflows.content";
|
|
294
|
+
|
|
295
|
+
export {
|
|
296
|
+
HARBOR_EXTENSION_ID,
|
|
297
|
+
HARBOR_PAGE_READER,
|
|
298
|
+
HARBOR_EXTENSION_VERSION,
|
|
299
|
+
MCP_APPS_PROTOCOL_VERSION,
|
|
300
|
+
HARBOR_METHODS,
|
|
301
|
+
HARBOR_EVENT_NOTIFICATION,
|
|
302
|
+
HARBOR_BAR_NOTIFICATION,
|
|
303
|
+
WORKFLOWS_LIST_LIMIT,
|
|
304
|
+
WORKFLOW_TASKS_MAX,
|
|
305
|
+
BAR_LIMITS,
|
|
306
|
+
VIEW_STATUSES,
|
|
307
|
+
HARBOR_VIEW_STATE_LIMITS,
|
|
308
|
+
HARBOR_VIEW_STATE_STRING_KEYS,
|
|
309
|
+
validateViewState,
|
|
310
|
+
HARBOR_VIEW_NAMES_ID,
|
|
311
|
+
HARBOR_VIEW_NAME_LIMITS,
|
|
312
|
+
validateViewNames,
|
|
313
|
+
UPDATE_MODEL_CONTEXT_PARAMS_SCHEMA,
|
|
314
|
+
HARBOR_EVENT_NAMES,
|
|
315
|
+
HARBOR_METHOD_PARAMS_SCHEMA,
|
|
316
|
+
HARBOR_GRANT_OF,
|
|
317
|
+
STANDARD_METHOD_GRANT_OF,
|
|
318
|
+
WORKFLOW_TITLE_GRANT
|
|
319
|
+
};
|