@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
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import type { CatalogSkip } from './package';
|
|
2
|
+
/**
|
|
3
|
+
* How harbor and the extensions server find each other, as data with no
|
|
4
|
+
* runtime imports: harbor's main process reads this module without any server code.
|
|
5
|
+
*/
|
|
6
|
+
/** The port the extensions server listens on; the providers' sign-in return addresses name it. */
|
|
7
|
+
export declare const EXTENSIONS_PORT = 8830;
|
|
8
|
+
/**
|
|
9
|
+
* The most header bytes a request to the extensions server or the sign-in
|
|
10
|
+
* return may carry. A browser sends every cookie it holds for `localhost`,
|
|
11
|
+
* whatever the port, so Node's 16 KB default refuses a return (431).
|
|
12
|
+
*/
|
|
13
|
+
export declare const EXTENSIONS_HEADER_BYTES: number;
|
|
14
|
+
/** What `GET /health` on the extensions server answers, so harbor can tell it from anything else on the port. */
|
|
15
|
+
export declare const EXTENSIONS_HEALTH: {
|
|
16
|
+
readonly service: "harbor-first-party-extensions";
|
|
17
|
+
readonly protocol: 1;
|
|
18
|
+
};
|
|
19
|
+
/** The environment the extensions server reads. An absent `enabled` means every shipped extension. */
|
|
20
|
+
export declare const EXTENSIONS_ENV: {
|
|
21
|
+
readonly port: "PORT";
|
|
22
|
+
readonly pagesDir: "EXTENSIONS_PAGES_DIR";
|
|
23
|
+
readonly accountScope: "EXTENSIONS_ACCOUNT_SCOPE";
|
|
24
|
+
/** Comma-separated extension names. */
|
|
25
|
+
readonly enabled: "EXTENSIONS_ENABLED";
|
|
26
|
+
/** Comma-separated names of the settings whose value came from Moku rather than harbor's environment. */
|
|
27
|
+
readonly fromMoku: "EXTENSIONS_SETTINGS_FROM_MOKU";
|
|
28
|
+
/** The sign-in return address the providers are given; absent means this server's own `/auth/callback`. */
|
|
29
|
+
readonly callbackAddress: "EXTENSIONS_CALLBACK_ADDRESS";
|
|
30
|
+
/** The connection this server runs for; `/health` names it, so harbor takes an answer only from its own child. */
|
|
31
|
+
readonly connection: "EXTENSIONS_CONNECTION";
|
|
32
|
+
/** The dev instance this server runs for. Its sign-in states name it, so the shared return reaches it. */
|
|
33
|
+
readonly instance: "EXTENSIONS_INSTANCE";
|
|
34
|
+
/** The installed catalog the server loads its extensions from (`tree/catalog.json`). */
|
|
35
|
+
readonly catalog: "EXTENSIONS_CATALOG";
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The messages on Node's IPC channel between harbor and an extensions server it started:
|
|
39
|
+
* `catalog_loaded`, `tools_changed`, `page_notification`, `saved_sign_in`, `extension_state` and
|
|
40
|
+
* `provider_account` from the server, `saved_sign_in_answer` and `extension_state_answer` back from harbor.
|
|
41
|
+
*/
|
|
42
|
+
export declare const EXTENSIONS_MESSAGE: {
|
|
43
|
+
readonly toolsChanged: "tools_changed";
|
|
44
|
+
readonly pageNotification: "page_notification";
|
|
45
|
+
readonly savedSignIn: "saved_sign_in";
|
|
46
|
+
readonly savedSignInAnswer: "saved_sign_in_answer";
|
|
47
|
+
readonly extensionState: "extension_state";
|
|
48
|
+
readonly extensionStateAnswer: "extension_state_answer";
|
|
49
|
+
readonly providerAccount: "provider_account";
|
|
50
|
+
readonly catalogLoaded: "catalog_loaded";
|
|
51
|
+
};
|
|
52
|
+
/**
|
|
53
|
+
* What the server loaded from its catalog, sent once before it serves: the extensions it runs, the entries the
|
|
54
|
+
* catalog itself refused, and the ones whose module did not import or export an extension. Names and package names only.
|
|
55
|
+
*/
|
|
56
|
+
export interface CatalogLoadedMessage {
|
|
57
|
+
type: typeof EXTENSIONS_MESSAGE.catalogLoaded;
|
|
58
|
+
loaded: string[];
|
|
59
|
+
skipped: CatalogSkip[];
|
|
60
|
+
failed: {
|
|
61
|
+
name: string;
|
|
62
|
+
package: string;
|
|
63
|
+
}[];
|
|
64
|
+
}
|
|
65
|
+
/** An extension's tool list changed, so harbor lists that extension's tools again. */
|
|
66
|
+
export interface ToolsChangedMessage {
|
|
67
|
+
type: typeof EXTENSIONS_MESSAGE.toolsChanged;
|
|
68
|
+
/** The extension's name, the path segment it is served at. */
|
|
69
|
+
extension: string;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Something an extension's server tells every open view of that extension. Harbor hands `name` and
|
|
73
|
+
* `params` to each view subscribed to its `server.notification` event, as sent.
|
|
74
|
+
*/
|
|
75
|
+
export interface PageNotificationMessage {
|
|
76
|
+
type: typeof EXTENSIONS_MESSAGE.pageNotification;
|
|
77
|
+
/** The extension's name, the path segment it is served at. */
|
|
78
|
+
extension: string;
|
|
79
|
+
name: string;
|
|
80
|
+
params: Record<string, unknown>;
|
|
81
|
+
}
|
|
82
|
+
/** What a page notification's `name` may be: the server's own word for it, as a tool's name is spelled. */
|
|
83
|
+
export declare const PAGE_NOTIFICATION_NAME: RegExp;
|
|
84
|
+
/** The longest a saved sign-in request's `id`, `slot` and `value` may be. Harbor refuses a request past them. */
|
|
85
|
+
export declare const SAVED_SIGN_IN_LIMITS: {
|
|
86
|
+
readonly id: 64;
|
|
87
|
+
readonly slot: 128;
|
|
88
|
+
readonly value: 262144;
|
|
89
|
+
};
|
|
90
|
+
/** What follows `<provider>-` in a provider's further slot: lowercase kebab-case, as a provider's name is. */
|
|
91
|
+
export declare const SLOT_SUFFIX: RegExp;
|
|
92
|
+
/**
|
|
93
|
+
* The server asks harbor to read, write or remove one saved sign-in. `slot` names it without any
|
|
94
|
+
* scope: harbor keeps it under the identity it started the server for. `id` is the server's own,
|
|
95
|
+
* unique among its requests still waiting for an answer.
|
|
96
|
+
*/
|
|
97
|
+
export type SavedSignInRequest = {
|
|
98
|
+
type: typeof EXTENSIONS_MESSAGE.savedSignIn;
|
|
99
|
+
id: string;
|
|
100
|
+
op: 'read' | 'remove';
|
|
101
|
+
slot: string;
|
|
102
|
+
} | {
|
|
103
|
+
type: typeof EXTENSIONS_MESSAGE.savedSignIn;
|
|
104
|
+
id: string;
|
|
105
|
+
op: 'write';
|
|
106
|
+
slot: string;
|
|
107
|
+
value: string;
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Harbor's answer to one request. A read answers the saved value, or null, and `move`: true only
|
|
111
|
+
* when the server may move this slot's old keychain items now. A write or remove that worked answers
|
|
112
|
+
* `ok` alone. `refused` is a request harbor does not take; `failed` is a store that failed.
|
|
113
|
+
*/
|
|
114
|
+
export type SavedSignInAnswer = {
|
|
115
|
+
type: typeof EXTENSIONS_MESSAGE.savedSignInAnswer;
|
|
116
|
+
id: string;
|
|
117
|
+
ok: true;
|
|
118
|
+
value: string | null;
|
|
119
|
+
move: boolean;
|
|
120
|
+
} | {
|
|
121
|
+
type: typeof EXTENSIONS_MESSAGE.savedSignInAnswer;
|
|
122
|
+
id: string;
|
|
123
|
+
ok: true;
|
|
124
|
+
} | {
|
|
125
|
+
type: typeof EXTENSIONS_MESSAGE.savedSignInAnswer;
|
|
126
|
+
id: string;
|
|
127
|
+
ok: false;
|
|
128
|
+
reason: 'refused' | 'failed';
|
|
129
|
+
};
|
|
130
|
+
/** The longest an extension state request's `key` and `value`, and a provider account, may be. Harbor refuses one past them. */
|
|
131
|
+
export declare const EXTENSION_STATE_LIMITS: {
|
|
132
|
+
readonly key: 128;
|
|
133
|
+
readonly value: 262144;
|
|
134
|
+
readonly account: 256;
|
|
135
|
+
};
|
|
136
|
+
/**
|
|
137
|
+
* Whose an extension's kept value is: `identity`, the harbor identity's for that extension;
|
|
138
|
+
* `provider_account`, the provider account signed in now as well, which harbor tracks from
|
|
139
|
+
* {@link ProviderAccountMessage} and never takes from a request.
|
|
140
|
+
*/
|
|
141
|
+
export type StateRetention = 'identity' | 'provider_account';
|
|
142
|
+
/**
|
|
143
|
+
* The server asks harbor to read, write or delete one of `extension`'s kept values. `id` is the
|
|
144
|
+
* server's own, unique among its requests still waiting for an answer.
|
|
145
|
+
*/
|
|
146
|
+
export type ExtensionStateRequest = {
|
|
147
|
+
type: typeof EXTENSIONS_MESSAGE.extensionState;
|
|
148
|
+
id: string;
|
|
149
|
+
extension: string;
|
|
150
|
+
op: 'get' | 'delete';
|
|
151
|
+
key: string;
|
|
152
|
+
retention: StateRetention;
|
|
153
|
+
} | {
|
|
154
|
+
type: typeof EXTENSIONS_MESSAGE.extensionState;
|
|
155
|
+
id: string;
|
|
156
|
+
extension: string;
|
|
157
|
+
op: 'set';
|
|
158
|
+
key: string;
|
|
159
|
+
value: string;
|
|
160
|
+
retention: StateRetention;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Harbor's answer to one request: a get answers the kept value, or null; a set or delete that
|
|
164
|
+
* worked answers `ok` alone. `refused` is a request harbor does not take; `failed` is a store that failed.
|
|
165
|
+
*/
|
|
166
|
+
export type ExtensionStateAnswer = {
|
|
167
|
+
type: typeof EXTENSIONS_MESSAGE.extensionStateAnswer;
|
|
168
|
+
id: string;
|
|
169
|
+
ok: true;
|
|
170
|
+
value: string | null;
|
|
171
|
+
} | {
|
|
172
|
+
type: typeof EXTENSIONS_MESSAGE.extensionStateAnswer;
|
|
173
|
+
id: string;
|
|
174
|
+
ok: true;
|
|
175
|
+
} | {
|
|
176
|
+
type: typeof EXTENSIONS_MESSAGE.extensionStateAnswer;
|
|
177
|
+
id: string;
|
|
178
|
+
ok: false;
|
|
179
|
+
reason: 'refused' | 'failed';
|
|
180
|
+
};
|
|
181
|
+
/** The provider account this connection's server is signed in as, sent after every restore, sign-in finish and sign-out. */
|
|
182
|
+
export interface ProviderAccountMessage {
|
|
183
|
+
type: typeof EXTENSIONS_MESSAGE.providerAccount;
|
|
184
|
+
/** The provider session's account key; null when signed out or none restored. */
|
|
185
|
+
account: string | null;
|
|
186
|
+
reason: 'restored' | 'signed_in' | 'signed_out';
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* The JSON Schema keyword that marks a tool input as a local file: a property
|
|
190
|
+
* carrying it, or an array whose items carry it. The AI fills it with a path
|
|
191
|
+
* on the person's computer; Harbor contains that path against the folders the
|
|
192
|
+
* person granted, reads the file, and hands the tool a {@link LocalFileWire}
|
|
193
|
+
* in its place, so the extension never sees a path. Only a first-party
|
|
194
|
+
* extension's marks are honoured.
|
|
195
|
+
*/
|
|
196
|
+
export declare const LOCAL_FILE_KEYWORD = "x-holokai-local-file";
|
|
197
|
+
/** A local file as Harbor hands it to an extension tool, in place of the path the AI gave. */
|
|
198
|
+
export interface LocalFileWire {
|
|
199
|
+
/** The file's name, without its folder. */
|
|
200
|
+
name: string;
|
|
201
|
+
/** The file's media type; `application/octet-stream` when Harbor does not know the type. */
|
|
202
|
+
mime_type: string;
|
|
203
|
+
/** The file's whole content, base64. */
|
|
204
|
+
content_base64: string;
|
|
205
|
+
}
|
|
206
|
+
/** The schema mark on a tool input that is a new file: Harbor picks where it goes and the tool sees only its name. */
|
|
207
|
+
export declare const FILE_TARGET_KEYWORD = "x-holokai-file-target";
|
|
208
|
+
/** The `_meta` key under which an answer carries the bytes for the call's file target. */
|
|
209
|
+
export declare const SAVED_FILE_META = "holokai/saved_file";
|
|
210
|
+
/** The most bytes a file may hold when it passes between an extension and Harbor. */
|
|
211
|
+
export declare const SAVED_FILE_MAX_BYTES: number;
|
|
212
|
+
/** What an extension receives for a marked target: the name alone, never the folder. */
|
|
213
|
+
export interface FileTargetWire {
|
|
214
|
+
name: string;
|
|
215
|
+
}
|
|
216
|
+
/** What an answer carries under `_meta[SAVED_FILE_META]`. */
|
|
217
|
+
export interface SavedFileWire {
|
|
218
|
+
mime_type: string;
|
|
219
|
+
content_base64: string;
|
|
220
|
+
}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import {createRequire as __harborCreateRequire} from 'node:module'; const require = __harborCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/runtime.ts
|
|
4
|
+
var EXTENSIONS_PORT = 8830;
|
|
5
|
+
var EXTENSIONS_HEADER_BYTES = 256 * 1024;
|
|
6
|
+
var EXTENSIONS_HEALTH = { service: "harbor-first-party-extensions", protocol: 1 };
|
|
7
|
+
var EXTENSIONS_ENV = {
|
|
8
|
+
port: "PORT",
|
|
9
|
+
pagesDir: "EXTENSIONS_PAGES_DIR",
|
|
10
|
+
accountScope: "EXTENSIONS_ACCOUNT_SCOPE",
|
|
11
|
+
/** Comma-separated extension names. */
|
|
12
|
+
enabled: "EXTENSIONS_ENABLED",
|
|
13
|
+
/** Comma-separated names of the settings whose value came from Moku rather than harbor's environment. */
|
|
14
|
+
fromMoku: "EXTENSIONS_SETTINGS_FROM_MOKU",
|
|
15
|
+
/** The sign-in return address the providers are given; absent means this server's own `/auth/callback`. */
|
|
16
|
+
callbackAddress: "EXTENSIONS_CALLBACK_ADDRESS",
|
|
17
|
+
/** The connection this server runs for; `/health` names it, so harbor takes an answer only from its own child. */
|
|
18
|
+
connection: "EXTENSIONS_CONNECTION",
|
|
19
|
+
/** The dev instance this server runs for. Its sign-in states name it, so the shared return reaches it. */
|
|
20
|
+
instance: "EXTENSIONS_INSTANCE",
|
|
21
|
+
/** The installed catalog the server loads its extensions from (`tree/catalog.json`). */
|
|
22
|
+
catalog: "EXTENSIONS_CATALOG"
|
|
23
|
+
};
|
|
24
|
+
var EXTENSIONS_MESSAGE = {
|
|
25
|
+
toolsChanged: "tools_changed",
|
|
26
|
+
pageNotification: "page_notification",
|
|
27
|
+
savedSignIn: "saved_sign_in",
|
|
28
|
+
savedSignInAnswer: "saved_sign_in_answer",
|
|
29
|
+
extensionState: "extension_state",
|
|
30
|
+
extensionStateAnswer: "extension_state_answer",
|
|
31
|
+
providerAccount: "provider_account",
|
|
32
|
+
catalogLoaded: "catalog_loaded"
|
|
33
|
+
};
|
|
34
|
+
var PAGE_NOTIFICATION_NAME = /^[a-z][a-z0-9_.-]{0,63}$/;
|
|
35
|
+
var SAVED_SIGN_IN_LIMITS = { id: 64, slot: 128, value: 262144 };
|
|
36
|
+
var SLOT_SUFFIX = /^[a-z][a-z0-9-]*$/;
|
|
37
|
+
var EXTENSION_STATE_LIMITS = { key: 128, value: 262144, account: 256 };
|
|
38
|
+
var LOCAL_FILE_KEYWORD = "x-holokai-local-file";
|
|
39
|
+
var FILE_TARGET_KEYWORD = "x-holokai-file-target";
|
|
40
|
+
var SAVED_FILE_META = "holokai/saved_file";
|
|
41
|
+
var SAVED_FILE_MAX_BYTES = 25 * 1024 * 1024;
|
|
42
|
+
export {
|
|
43
|
+
EXTENSIONS_ENV,
|
|
44
|
+
EXTENSIONS_HEADER_BYTES,
|
|
45
|
+
EXTENSIONS_HEALTH,
|
|
46
|
+
EXTENSIONS_MESSAGE,
|
|
47
|
+
EXTENSIONS_PORT,
|
|
48
|
+
EXTENSION_STATE_LIMITS,
|
|
49
|
+
FILE_TARGET_KEYWORD,
|
|
50
|
+
LOCAL_FILE_KEYWORD,
|
|
51
|
+
PAGE_NOTIFICATION_NAME,
|
|
52
|
+
SAVED_FILE_MAX_BYTES,
|
|
53
|
+
SAVED_FILE_META,
|
|
54
|
+
SAVED_SIGN_IN_LIMITS,
|
|
55
|
+
SLOT_SUFFIX
|
|
56
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The page for one sign-in. `connected` is the name of what was just connected, as a person knows it
|
|
3
|
+
* (a provider such as Linear, or a connector); null is a sign-in that failed. `app` is the app's name.
|
|
4
|
+
*/
|
|
5
|
+
export declare function authenticationPage(app: string, connected: string | null): string;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import {createRequire as __harborCreateRequire} from 'node:module'; const require = __harborCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/holokai-logo.ts
|
|
4
|
+
var HOLOKAI_LOGO_PNG = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAjYAAAI2CAMAAAB0aDIHAAAAM1BMVEUAAAAtha0tha0tha0tha0tha0tha0tha0tha0tha0tha0tha0tha0tha0tha0tha0tha2BV9aoAAAAEHRSTlMAgL9AEO9gnzDfIM+PcK9Q+JwXawAAEkVJREFUeNrs3W1OAjEUhtGLMHyJcve/WiWTSCIR5i3885xFPO1ty1AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAPrY0Ho3L0vyOy63woyU3dvCxL7/nYoSJy65YbUoS+mTcFi656tCpI5Sm5ITS03pLbdckPqrVtuCB175uyGwKlnzm4ITC03DGyI5YaBDbHckN9iyg1DJ8RX3t2QTN9X64Jg+p7tCu7aTH3LDQP3rfqHGway91ltBic96rt1LghiM/sseBAbm2Keic3VR0Eam34vSGIz8yEB7sTGKkUeG6sUA7GxSpHHxirFQGysUuSxsUrxbGy8DSWKjXspXhWbngoexcaTYuLYeOPH4tgYwRmPjUc3DMXG01CWxcbmhtfExq96yWNzq+B3bJzcMBobjydIY+Naiidj40e9LIiNAz/S2NgTE9lcYmNPTGbVf/NdLR58PMs5MXlsnBPzTGz8fIFlsTFKMRwbbycIYpM5Ff/dHBujFGP/ruBWivDfFUzgDMTGBE4eGxM4L4iN7058sXcvuY3DUBBFKVKfSLHjt//VNgJk1JOklHhg+5w1XBAGS5L5wWHj4oZEv2c2x3X66J/e6z/90+bMelCjAunFzaW+8+45wYfU75jNVqWbpzQqEN73rX4kPateZ7VvbWaKJzXqjtl02TypvU47ZPOq1jpvyOZV7bIhtpZsSM17/cIqm9c01W9MsnlJ8yIbYlPJhvywkQ2xrWRD6ijZENtkQ+wo2RDrsiE2SjbEumyIjZINsV02xNaSDbFdNsSuJRvyDVM2xKaSDflhIxtil5INqaNkQ2yTDbG3kg2xLhtio2RDrMuG2K1kQ2yXDbG1ZENq3mVDbCrZkG+YsiF2KdmQOko2xDbZEHsr2RDrsiE2SjbE3mVDbC3ZENtlQ+xaJRvuNyssF9mQbpgfc/u7bCbZPLJjqZ86mmxIZ4WtyYZ4wzxkQzwrbE02xLPCIRtOHDayIZ4VDtkQzwpbkw3prLDMsiGeFaYmG9JZYZllQzwrTE02pLPCPsuGeFZYm2xIb/r2JhviWWHIhviw6U02xLPCkA0nZgXZEL+tcMiGE7OCbMhnBdkQzwprkw3xrNBkQ/wRpCEbTtz0yYb8bQXZEH8E6dJkQzorLLNsiGeFa5MN6U3f3mRDPCsM2RAfNr3JhvgB4jfZcOIjSLIhvelbZtkQzwpTkw3pYbM32RDf9N1kQzwr9CYb4r9sHrIhvunbmmyIZ4VDNsQ3fVOTDemssMyy4ctHcNjIhvymTzbEs8IqG77cgsNGNsSzwpAN8XdJepMN+QPEsvnH3r3kuA0DURQtkvrLVmr/qw2VZJL0QH4M0RKlexbQDRgXb+ASZBQ8QEw2kN9LksgG8lkhGtlA/xVVskHBDxuSDfT3kpAN/liFsSEb6L+iSjYoeN052UD9ps+nitkMZNMy4aywWsVsEtm0TBiblx0byOYJFv9cTIfeTjZPMHpdZPMEk3+KbFBwViAbFJwVyAb6WcHJBvpZwckG4llhRzb4LbiAbCCeFXZkg19eriAb7BaXkA0Kzgpkg4KzAtmg4KxANig4K5ANCs4KZAMbXEU2sNlVZIPgpyOb5nS9n45smnOBsSGb5ix+AWTTmtEvgGwac4mxIZvWRL8CsmnL6WcFsmnRNcaGbNpy/lmBbBp0/lmBbNpzlbEhm5Zc4axANs25wlmBbFpznbEhm4a8/DLIphmLS9Z3OhC+6MnmdkYXbJ39i1c+PtHkisW+4AWzTxRdEKxMTzb3klzQd6Vtks29iGNDNsgGcWwKjWRzK7M2NqUC2dyJODZkA/msMBjZQL1hzkY20MeGbFAwNmQD+YY5kA3kG+ZqZAN5bBLZYLe5IBrZIEv62JTbyOYmYsnYcMp8OHFsyAby2GxGNsgGVyxkA/mBifH//x3Z3EH52PAI+nN1vTY2ZIMsuGIhG2RL74JgZINsrPAoqKQjm/Yl/+6xSWTTvqiNDdkgG8SxIRuYdbM2NmSDLLhiMLKB+k3fbGSDbHTFQDaQHyCejWygjw3ZIEuuWI1skMWSR0E5Ljxc8pLnzjllPtzoikQ2yJazxsbWatm8yea7BRf00wkzN1VKPxiqmc+qxpa+2kjEzy5pqEarpqop+qH+1dmxbvQj22KoR6qmupTeIYtfbCH7kSbhLw1ht8W/hN07JaamrvAxPnkAAAAAAAAAAAAAAH6ydy/ajYJQFIbhoKCCet7/aWcmbSadNpmo8ULk/96gq3vBFqIHADBV4y7sF979we8G8VV3+fFhEBF9RkSs9SSoYM5dsqKLtJIs4SlI5UabJOrrgsH5Vc7bQXQ1dWNwZp2zSWr9g9Tgucr1SXQT3uCEutEOrV6RGjzTeCu1bioZnEjnn29KPEThphsfrTGkBne5tXoM30IpROND1Cdow7ip3IRtidTgpulDqweoz5qaoBrFemfOqhqt6GScDc8cGhUHO57tr5y3yJCaBd/pu4XnFFf8l0XmWPHEqTGj/iTJv/Of7Pqh1cPFc39pbNDfzpKdxqeoWTh5aj7njb1/drrjt6WCUvN8wEItufedj5uCnJw/NcYkfa4dbJYfZM0vMaWkZvqkgzb0GR3wuD5ltCuVlxrT1DpdPD47zWglg2elwlMza1L8bc/qzO4q57MOzEWddxFcU9AFakn95oXnlpchwxJT2Nnwd1XUxSTYcav0NM7bJJkcxXDn/UOjr4qSbL/Oy8/OjdYGybTw8rvhL3pdTSsyXN+hrya+V/0x1mWQ91pZGIdnBt1OLfe8fUZ+akt5iPqrOt3/cBLq8Iuat3hQyVpRdfjKK3i1Zb6k4HR4vrd85M1FicXmQ5X7wX3Oiiw21GKKzXKjgmLD49RDFBtz+GV48QouNuRmscGAWwauorid2kHpxYbcLNEbkBuKDcd+91BsPpGbGSg2f5GbGSg222m41qTY0IuvKDb/IjdTUGy+IzcTcBV1F/dT/8VvbO4hN3uKBvz+Zq6aOvyY4wCHX2Yt0FCMqcMLVIOCgT/z9Qoeorhp4IMkj7BR/cDrLfvreaIiNQt0vCHOgQ0LDgc2D7HgKKnJgC9+wWlJzQJV4d9Oog2zU5GaPblib6kCqXmFL/PUuLBPnBtDcBgGnwdf2FZ16gHNe3IlleNErVlNl2o9RC0iwd4EEdENtc5gRZUX3ZGItY/nxnTO2yAbJNmy1Kyus61urhU7eVRe5XxaMzxDOWMM99WkDZOzcLZi5WyI+jphf9pQY6OurX19cr3rX8tOYKXZWuVDqyuJ4TKv/tiR4dHTafbR+RRfX2Ias77G21mFJ/YsNPtyfRCdTwY7OrOpyk161IppZJ05Rud6KzLtDMYunQm9fKx4kHh/rUu9IzLH665Td/8VrLW9c84cqbmMBP40Ose2BAAAAAAAAAAAAAAAAAAAAAAA8Iu9c9uWGoQBKC3h3kv+/2t1qctlM4yQlOqI2W9ezjQHdmlDGFAURVEURVEURVEURVEURVEURVEURVEURVEURVEURVGUv0C0P/iIE0d/RKP7l/PJ9gKY32IvsPreLmuiZ7GQq3EvLQdOcmBLOhZrHogGLKEz8rxQDKH3ktk8wYIXGm2HF1bTCfgDq6zk/BzGpeXkvWCVw8PoaApS+uwMSDm7jcYL3337B7WxAX/DYf+sNueKvyGMjSYgxZsu3Gtk/5c2dsUGyf85bXzin24pj8ZLO//EFxz8R9rYFTso9s9oYxN2sMZB0URElIV94Cv+v9EGNuwkwPPa5AM7WYZEkx0SCpguACsc/4s2NmE3zj6tzemwmxLvRwMFCS6aPjzWgP9DmwVZLM9qE5CFvx1NYCRRhBVr7P+DNhCQyQHPaQMFmYSbDbEzTCRkrFL+A22gIJsCT2mTBdGEWw1hmR4S46rk6bWBggIKPKNNdigg3GiI6BDFERess82uDRQUUWC8NvJogrghoDB+M0rEN6TZtQkoJDygDRQUskmjOThJFGXDd8S5tVmwStm8hR8nsG8FqyzjtQmNaLLdQ8IqXhbNwkqiKAnfEabWxmKFtGRa5E1YwY7WZq9Gs4O5EDeHr7gsicZX9OvnxLe4mbUBV6s79VaIEozVJrrOYgYstf/JbIj6FcOoCaZzYm2Oxmx9o/qwjdVmRYrbTR2oRc6OBhIviaI4fE+YVxtbKRywpvztSG08q3DgK48pbjSrLImiIdSBabUpjX4SzHDItQFmOdG+PmCY0QROEtUeq8kf/aza+NrLCs8bO06bhXvrR6RkVjSemURRgN5y5EqzapPYN1tEwirVpj3YOGA/JAInmthKopiX32l75jm1OQXNtiPBSrVpf7Igk8n90QA7iaIUeu2NeDSnNiteOSTZThilTUJ+G8LLD3VHA6UxcHKL38fL35QptclIyKIqDIzRxorW43q8krobIrCTqEZ3+NfxJ86ozS4bo2l7e6E27Y+VDH6xM5q9kUTxh0d4/dRtRm0KXsmyou8xRhtHelE4dbL1RWMbSRS/HY7KAJ4m1AbYbzZ13ZxAm7aMm/CeL13RZNfIBvjFb19rGzufNideOaVzo3aENjteicLeQ+iIBkojiRIMj1D7NcJ82ix4RZpB4D5Cm4MO771YgcQHP4lq3XWh2jZuPm3WUQshwwhtkvAZRS+GSyua5nS0KDM46/r7z9MmLL+l1XdJHHJofLREG/GrBtX/aEZzNpIoyZy2e/MEPz5Pmwa8vsVTvphqgDaW8WpD2JgSN+pqshe88C7RgMm0ifKOss9rY8QSu0Y0JTWSKFHx+3z3D34ybfgd1TZOro2nff+YxHgziWq9+npiqWrzriPsfW3om9jj2siTqEaiDXglqzYTaiNJon5ffAp4YVdtPlIbc0ubaLi0iggn+TfV5iO1AbE28p7afjfL5IiYqs0naiN/SMnzqPQ7MwJxSrWZUBvJXF/87XPoJBdQbeo/mu9rc+KVJ7URrQfjjCcOL5wzaZPxiv1Xp/uW1isosUSWTDHeXqhV4bO0uVmTwiteHIcboE3kSiwvLuxICfeK36Xxyzj4KG14FfBmzfmjSpn7g/X4gJTtVmFhbzWtn0mbg94z3SRuR8k7vw3wF04USTrFmQfe8MIxkzZLvVbbJj6xTCtIi1Inf5kWOKREefG7tFsIJtLGNu44yfpNuTa79OUmCBaFxhdvXBYv8Nnb4/E+kTZGWtNLTyxBz8LXVHCSJei+kU5xIvX2FfoCMJM2B17JsvQ7SLRpywiy9fBLXzSbvBK+I588kTZedoOvz3y9bpMVi5Lw63WHOA1PyGebSBsYsbklOjNGmyiavN2lX+YFaToVUUCaSBsTBOM0pKe2DqD9eEg2N9m7o4lO9sXMDSXEibSxSFgErRZHaeORcArOcoL+aKysqplQQphIG7Oy7zf/3LZIxrF7cbm1LdIuSadOFOFm0sYy7rfnN2HbuVvC+ZubsAXBwSwBZZwTaWNWjjfyLR+lOUrJPGs2ZjSFn045lBFm0iYixXlOP2Eeqc3J2u52QYoDZjTguOeHeZQCE2lTywsOeG4DaYp8c+284gueHU3kbl5+oBQ/kzaQ8AW3wOjt6uvIt/KHBV9ZBdF4XjoFKGadSZv67JXbYtfhGHGwNubsOjgkhmo0IIlmYy0S3akLv6HglTyTNsZjlRR2+82KOPAonjbb+4OB8o9oDodVrCyalZOGExUO1jZAU2nz/DFkxdaJQ18evLAhgJFOURNOTvGuzKWNCeOPGexi/YxDDytVhkWwiLo9jse5tJF5E8x4beTehHubGFI8fzVs+/15m0wbiTfBjNdG7k24uUEzxcWe9OFk7kc4mzb8su5mxmsjP8ze314KQHG53UqOvaXqbNoY75CB82a4NvLvEbrbDQGlK51yzEIE4JUwnTYmFuymRPOsNsYm7GYFVjRr5zzjMWAj54P4PZ82xiwOu3CLMU9rY2DjDXzji3O4NR5liT976SfUxuSAHYRsBmtTJ67YwQZGqE171tO3EyNmvfyYURsijlAauTYUuz4RzdqfFcTGNIzgS1xTamNM3hy+xW3ZmGe1qRaf6qQFJNGsjNOkYyOd5j+lJtXmK2dI9SLVaRrwtWlz1s1JpNg6oCEg/TadyqLtBkjw5W9oY5cLuSHZBW8YZL+tCX+S1s1nlt8NeCFFv63u12iWUxBN+6pxecGan+TlSl8IfrlCG5pebgLAfgPMR/A9mikaVlEURVEURVEURVEURVEURVEURVEU5Ut7cEgAAAAAIOj/azfYAQAAAAAAAAAAAAAAAAAAgCEsMZPttaKNCgAAAABJRU5ErkJggg==";
|
|
5
|
+
|
|
6
|
+
// src/sign-in-page.ts
|
|
7
|
+
var FONT = `'Poppins', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif`;
|
|
8
|
+
var BACKGROUND = [
|
|
9
|
+
"radial-gradient(circle at 82% 12%, rgba(73, 195, 232, 0.2), transparent 46%)",
|
|
10
|
+
"radial-gradient(circle at 10% 94%, rgba(255, 122, 77, 0.12), transparent 42%)",
|
|
11
|
+
"linear-gradient(152deg, #04212c 0%, #0a4458 52%, #062834 100%)"
|
|
12
|
+
].join(", ");
|
|
13
|
+
var STYLE = `
|
|
14
|
+
html { background: #062834; }
|
|
15
|
+
body {
|
|
16
|
+
margin: 0;
|
|
17
|
+
min-height: 100vh;
|
|
18
|
+
box-sizing: border-box;
|
|
19
|
+
display: grid;
|
|
20
|
+
place-items: center;
|
|
21
|
+
padding: 32px 16px;
|
|
22
|
+
background: ${BACKGROUND};
|
|
23
|
+
color: #ffffff;
|
|
24
|
+
font-family: ${FONT};
|
|
25
|
+
font-size: 16px;
|
|
26
|
+
line-height: 1.55;
|
|
27
|
+
}
|
|
28
|
+
.card {
|
|
29
|
+
display: grid;
|
|
30
|
+
gap: 12px;
|
|
31
|
+
justify-items: center;
|
|
32
|
+
text-align: center;
|
|
33
|
+
max-width: 420px;
|
|
34
|
+
padding: 36px 32px;
|
|
35
|
+
background: rgba(6, 35, 48, 0.55);
|
|
36
|
+
border: 1px solid rgba(73, 195, 232, 0.25);
|
|
37
|
+
border-radius: 12px;
|
|
38
|
+
box-shadow: 0 22px 60px rgba(6, 35, 48, 0.35);
|
|
39
|
+
backdrop-filter: blur(6px);
|
|
40
|
+
}
|
|
41
|
+
.logo { display: block; width: 72px; height: 72px; }
|
|
42
|
+
.check { width: 60px; height: 60px; }
|
|
43
|
+
.check circle { fill: none; stroke: #49c3e8; stroke-width: 3; }
|
|
44
|
+
.check path { fill: none; stroke: #49c3e8; stroke-width: 3.5; stroke-linecap: round; stroke-linejoin: round; }
|
|
45
|
+
.title { margin: 0; font-size: 1.5rem; font-weight: 700; line-height: 1.2; color: #ffffff; }
|
|
46
|
+
.sub { margin: 0; font-weight: 500; color: #49c3e8; }
|
|
47
|
+
.note { margin: 0; font-size: 0.95rem; color: rgba(255, 255, 255, 0.78); }
|
|
48
|
+
@media (prefers-reduced-motion: no-preference) {
|
|
49
|
+
.check circle { stroke-dasharray: 170; animation: ring 0.6s ease-out both; }
|
|
50
|
+
.check path { stroke-dasharray: 40; animation: tick 0.4s ease-out 0.5s both; }
|
|
51
|
+
.rise { animation: rise 0.5s ease-out both; }
|
|
52
|
+
.rise.d1 { animation-delay: 0.25s; }
|
|
53
|
+
.rise.d2 { animation-delay: 0.45s; }
|
|
54
|
+
.rise.d3 { animation-delay: 0.65s; }
|
|
55
|
+
@keyframes ring { from { stroke-dashoffset: 170; } to { stroke-dashoffset: 0; } }
|
|
56
|
+
@keyframes tick { from { stroke-dashoffset: 40; } to { stroke-dashoffset: 0; } }
|
|
57
|
+
@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
|
|
58
|
+
}
|
|
59
|
+
`;
|
|
60
|
+
var CHECK = '<svg class="check" viewBox="0 0 60 60" aria-hidden="true"><circle cx="30" cy="30" r="27"/><path d="M19 31 l8 8 l15-16"/></svg>';
|
|
61
|
+
function authenticationPage(app, connected) {
|
|
62
|
+
const name = escapeHtml(app);
|
|
63
|
+
const title = connected === null ? "Authentication failed" : "Authentication successful";
|
|
64
|
+
const lines = connected === null ? [
|
|
65
|
+
`<h1 class="title rise d1">${title}</h1>`,
|
|
66
|
+
`<p class="note rise d2">Return to ${name} and try again.</p>`
|
|
67
|
+
] : [
|
|
68
|
+
CHECK,
|
|
69
|
+
`<h1 class="title rise d1">${title}</h1>`,
|
|
70
|
+
`<p class="sub rise d2">${escapeHtml(connected)} is now connected to ${name}.</p>`,
|
|
71
|
+
`<p class="note rise d3">You can close this window and return to ${name}.</p>`
|
|
72
|
+
];
|
|
73
|
+
return [
|
|
74
|
+
"<!doctype html>",
|
|
75
|
+
'<html lang="en">',
|
|
76
|
+
"<head>",
|
|
77
|
+
'<meta charset="utf-8">',
|
|
78
|
+
'<meta name="viewport" content="width=device-width, initial-scale=1">',
|
|
79
|
+
`<title>${title}</title>`,
|
|
80
|
+
`<style>${STYLE}</style>`,
|
|
81
|
+
"</head>",
|
|
82
|
+
"<body>",
|
|
83
|
+
'<main class="card">',
|
|
84
|
+
`<img class="logo" src="${HOLOKAI_LOGO_PNG}" alt="${name}" width="72" height="72">`,
|
|
85
|
+
...lines,
|
|
86
|
+
"</main>",
|
|
87
|
+
"</body>",
|
|
88
|
+
"</html>"
|
|
89
|
+
].join("\n");
|
|
90
|
+
}
|
|
91
|
+
function escapeHtml(text) {
|
|
92
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
93
|
+
}
|
|
94
|
+
export {
|
|
95
|
+
authenticationPage
|
|
96
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@holokai/harbor-sdk",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "The harbor extension to MCP Apps: the public contract a hosted view speaks, and a thin client over @modelcontextprotocol/ext-apps.",
|
|
6
|
+
"exports": {
|
|
7
|
+
".": {
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"default": "./dist/index.js"
|
|
10
|
+
},
|
|
11
|
+
"./contract": {
|
|
12
|
+
"types": "./dist/contract.d.ts",
|
|
13
|
+
"default": "./dist/contract.js"
|
|
14
|
+
},
|
|
15
|
+
"./manifest": {
|
|
16
|
+
"types": "./dist/manifest.d.ts",
|
|
17
|
+
"default": "./dist/manifest.js"
|
|
18
|
+
},
|
|
19
|
+
"./runtime": {
|
|
20
|
+
"types": "./dist/runtime.d.ts",
|
|
21
|
+
"default": "./dist/runtime.js"
|
|
22
|
+
},
|
|
23
|
+
"./sign-in-page": {
|
|
24
|
+
"types": "./dist/sign-in-page.d.ts",
|
|
25
|
+
"default": "./dist/sign-in-page.js"
|
|
26
|
+
},
|
|
27
|
+
"./package": {
|
|
28
|
+
"types": "./dist/package.d.ts",
|
|
29
|
+
"default": "./dist/package.js"
|
|
30
|
+
},
|
|
31
|
+
"./package.json": "./package.json"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@modelcontextprotocol/ext-apps": "^2.0.0"
|
|
35
|
+
},
|
|
36
|
+
"peerDependenciesMeta": {
|
|
37
|
+
"@modelcontextprotocol/ext-apps": {
|
|
38
|
+
"optional": true
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"devDependencies": {
|
|
42
|
+
"@modelcontextprotocol/client": "2.4.0",
|
|
43
|
+
"@modelcontextprotocol/core": "2.4.0",
|
|
44
|
+
"@modelcontextprotocol/ext-apps": "^2.0.0",
|
|
45
|
+
"@types/node": "^24.0.0",
|
|
46
|
+
"typescript": "^6.0.3",
|
|
47
|
+
"vitest": "^4.1.12"
|
|
48
|
+
},
|
|
49
|
+
"publishConfig": {
|
|
50
|
+
"access": "restricted"
|
|
51
|
+
},
|
|
52
|
+
"files": [
|
|
53
|
+
"dist"
|
|
54
|
+
],
|
|
55
|
+
"scripts": {
|
|
56
|
+
"typecheck": "tsc --noEmit"
|
|
57
|
+
}
|
|
58
|
+
}
|