@hosty-sdk/app 0.14.0 → 0.16.1
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 +25 -0
- package/dist/assistant.d.ts +67 -0
- package/dist/assistant.js +79 -0
- package/dist/index.d.ts +5 -11
- package/dist/index.js +5 -11
- package/dist/install-react.js +1 -1
- package/dist/install.d.ts +3 -0
- package/dist/server.js +1 -1
- package/package.json +8 -4
package/README.md
CHANGED
|
@@ -180,3 +180,28 @@ an administrator browser login issued on a dedicated Core hostname, separate fro
|
|
|
180
180
|
hosts. Existing sessions need a fresh login. Requests expire after 15 minutes and Core restart
|
|
181
181
|
invalidates them. Closing your UI does not cancel a confirmed operation; status is the authority.
|
|
182
182
|
Never automatically retry an execution after a lost response.
|
|
183
|
+
|
|
184
|
+
## Assistant handoffs
|
|
185
|
+
|
|
186
|
+
`@hosty-sdk/app/assistant` exposes the version-1 client. Discover the confirmed assistant's
|
|
187
|
+
interface URL, version and capabilities through Core. Keep request and attachment identities
|
|
188
|
+
until the operation is resolved; do not create a new ID after an uncertain response.
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
import { AssistantClient, createAssistantRequestId } from "@hosty-sdk/app/assistant";
|
|
192
|
+
const client = new AssistantClient(interfaceUrl, { version: 1, capabilities: ["attachments"] }, issueToken);
|
|
193
|
+
const requestId = createAssistantRequestId(); // persist with the intent before the first request
|
|
194
|
+
const handoff = await client.prepare({ requestId, prompt: "Inspect this", appIds: [appId] });
|
|
195
|
+
// Optional: client.upload(handoff.handoffId, stableAttachmentId, file, file.name, { signal })
|
|
196
|
+
const finalized = await client.finalize(handoff.handoffId, []);
|
|
197
|
+
// Validate result.open with resolveAssistantDestination against Core's declared UI surfaces.
|
|
198
|
+
// Opening never submits the prompt again. client.status/cancel inspect or cancel preparations.
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
The receiver owns draft/immediate-start policy. `askAssistant(text)` remains the small embedded-app
|
|
202
|
+
message helper; Shell turns that message into a handoff using its verified mounted app ID. Its boolean
|
|
203
|
+
result reports message delivery to the embedder, not execution. Files use the separate `attachments`
|
|
204
|
+
capability and raw upload API. See [the complete contract](../../docs/features/hosty-harness-rename/feature.md).
|
|
205
|
+
|
|
206
|
+
Assistant control requests use a 60-second deadline. Uploads accept an optional caller-owned
|
|
207
|
+
`AbortSignal` and have no SDK-imposed deadline, so slow valid transfers are not aborted after a minute.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/** Version 1 of the manifest-discovered assistant handoff. */
|
|
2
|
+
export type AssistantContract = {
|
|
3
|
+
version?: number | null;
|
|
4
|
+
capabilities?: readonly string[] | null;
|
|
5
|
+
};
|
|
6
|
+
export type AssistantDestination = {
|
|
7
|
+
endpoint: string;
|
|
8
|
+
path: string;
|
|
9
|
+
};
|
|
10
|
+
export type AssistantHandoffResult = {
|
|
11
|
+
conversationId: string;
|
|
12
|
+
disposition: "draft" | "accepted";
|
|
13
|
+
open: AssistantDestination;
|
|
14
|
+
dispatchId?: string;
|
|
15
|
+
};
|
|
16
|
+
export type AssistantHandoff = {
|
|
17
|
+
handoffId: string;
|
|
18
|
+
requestId: string;
|
|
19
|
+
conversationId: string;
|
|
20
|
+
state: "pending" | "finalized" | "cancelled" | "expired";
|
|
21
|
+
createdAt: string;
|
|
22
|
+
expiresAt: string;
|
|
23
|
+
replayUntil: string;
|
|
24
|
+
attachments: Array<{
|
|
25
|
+
attachmentId: string;
|
|
26
|
+
name: string;
|
|
27
|
+
mediaType: string;
|
|
28
|
+
sizeBytes: number;
|
|
29
|
+
sha256: string;
|
|
30
|
+
}>;
|
|
31
|
+
limits?: {
|
|
32
|
+
maxFileBytes: number;
|
|
33
|
+
maxFiles: number;
|
|
34
|
+
maxTotalBytes: number;
|
|
35
|
+
};
|
|
36
|
+
result?: AssistantHandoffResult;
|
|
37
|
+
executionState?: "queued" | "running" | "completed" | "failed" | "unknown";
|
|
38
|
+
};
|
|
39
|
+
export declare function assistantContractError(contract: AssistantContract): string | null;
|
|
40
|
+
/** Time-bearing request identities let a provider reject retries after replay records expire. */
|
|
41
|
+
export declare function createAssistantRequestId(now?: number): string;
|
|
42
|
+
/** Resolve only inside a declared UI surface on the selected app's resolved endpoint. */
|
|
43
|
+
export declare function resolveAssistantDestination(destination: AssistantDestination, surfaces: readonly {
|
|
44
|
+
endpoint?: string | null;
|
|
45
|
+
path?: string | null;
|
|
46
|
+
url?: string | null;
|
|
47
|
+
}[]): string;
|
|
48
|
+
export declare class AssistantClient {
|
|
49
|
+
private readonly baseUrl;
|
|
50
|
+
private readonly contract;
|
|
51
|
+
private readonly token;
|
|
52
|
+
constructor(baseUrl: string, contract: AssistantContract, token: (refresh: boolean) => Promise<{
|
|
53
|
+
token: string;
|
|
54
|
+
}>);
|
|
55
|
+
private request;
|
|
56
|
+
prepare(input: {
|
|
57
|
+
requestId: string;
|
|
58
|
+
prompt: string;
|
|
59
|
+
appIds: string[];
|
|
60
|
+
}): Promise<AssistantHandoff>;
|
|
61
|
+
upload(handoffId: string, attachmentId: string, file: Blob, name: string, options?: {
|
|
62
|
+
signal?: AbortSignal;
|
|
63
|
+
}): Promise<AssistantHandoff["attachments"][number]>;
|
|
64
|
+
finalize(handoffId: string, attachmentIds: string[]): Promise<AssistantHandoff>;
|
|
65
|
+
status(handoffId: string): Promise<AssistantHandoff>;
|
|
66
|
+
cancel(handoffId: string): Promise<AssistantHandoff>;
|
|
67
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export function assistantContractError(contract) {
|
|
2
|
+
return contract.version !== 1 || !Array.isArray(contract.capabilities)
|
|
3
|
+
? "This assistant does not support the required assistant interface version 1." : null;
|
|
4
|
+
}
|
|
5
|
+
/** Time-bearing request identities let a provider reject retries after replay records expire. */
|
|
6
|
+
export function createAssistantRequestId(now = Date.now()) {
|
|
7
|
+
const bytes = crypto.getRandomValues(new Uint8Array(16));
|
|
8
|
+
let time = now;
|
|
9
|
+
for (let i = 5; i >= 0; i--) {
|
|
10
|
+
bytes[i] = time % 256;
|
|
11
|
+
time = Math.floor(time / 256);
|
|
12
|
+
}
|
|
13
|
+
bytes[6] = (bytes[6] & 15) | 112;
|
|
14
|
+
bytes[8] = (bytes[8] & 63) | 128;
|
|
15
|
+
const hex = Array.from(bytes, b => b.toString(16).padStart(2, "0")).join("");
|
|
16
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
17
|
+
}
|
|
18
|
+
/** Resolve only inside a declared UI surface on the selected app's resolved endpoint. */
|
|
19
|
+
export function resolveAssistantDestination(destination, surfaces) {
|
|
20
|
+
if (!destination || typeof destination.path !== "string" || !destination.path.startsWith("/") || destination.path.startsWith("//") || /[\\\u0000-\u001f]/.test(destination.path))
|
|
21
|
+
throw new Error("Invalid assistant UI destination.");
|
|
22
|
+
if (/%(?:2f|5c|25)/i.test(destination.path.split(/[?#]/, 1)[0]))
|
|
23
|
+
throw new Error("Invalid assistant UI destination.");
|
|
24
|
+
for (const surface of surfaces) {
|
|
25
|
+
if (surface.endpoint !== destination.endpoint || !surface.url)
|
|
26
|
+
continue;
|
|
27
|
+
const base = new URL(surface.url);
|
|
28
|
+
const url = new URL(destination.path, base.origin);
|
|
29
|
+
const root = new URL(surface.path || "/", base.origin).pathname.replace(/\/$/, "") || "/";
|
|
30
|
+
if (url.origin === base.origin && (root === "/" || url.pathname === root || url.pathname.startsWith(`${root}/`)))
|
|
31
|
+
return url.href;
|
|
32
|
+
}
|
|
33
|
+
throw new Error("The assistant returned a destination outside its declared UI.");
|
|
34
|
+
}
|
|
35
|
+
export class AssistantClient {
|
|
36
|
+
baseUrl;
|
|
37
|
+
contract;
|
|
38
|
+
token;
|
|
39
|
+
constructor(baseUrl, contract, token) {
|
|
40
|
+
this.baseUrl = baseUrl;
|
|
41
|
+
this.contract = contract;
|
|
42
|
+
this.token = token;
|
|
43
|
+
}
|
|
44
|
+
async request(route, init = {}, controlDeadline = true) {
|
|
45
|
+
const incompatible = assistantContractError(this.contract);
|
|
46
|
+
if (incompatible)
|
|
47
|
+
throw new Error(incompatible);
|
|
48
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
49
|
+
const grant = await this.token(attempt > 0);
|
|
50
|
+
const headers = new Headers(init.headers);
|
|
51
|
+
headers.set("authorization", `Bearer ${grant.token}`);
|
|
52
|
+
if (typeof init.body === "string")
|
|
53
|
+
headers.set("content-type", "application/json");
|
|
54
|
+
const response = await fetch(`${this.baseUrl.replace(/\/$/, "")}${route}`, { ...init, headers, signal: init.signal ?? (controlDeadline ? AbortSignal.timeout(60_000) : undefined) });
|
|
55
|
+
if (response.status === 401 && attempt === 0)
|
|
56
|
+
continue;
|
|
57
|
+
const body = await response.json().catch(() => null);
|
|
58
|
+
if (!response.ok)
|
|
59
|
+
throw new Error(body?.message || `Assistant request failed (${response.status}).`);
|
|
60
|
+
return body;
|
|
61
|
+
}
|
|
62
|
+
throw new Error("Assistant authorization expired.");
|
|
63
|
+
}
|
|
64
|
+
prepare(input) {
|
|
65
|
+
return this.request("/handoffs", { method: "POST", body: JSON.stringify(input) });
|
|
66
|
+
}
|
|
67
|
+
upload(handoffId, attachmentId, file, name, options = {}) {
|
|
68
|
+
if (!this.contract.capabilities?.includes("attachments"))
|
|
69
|
+
throw new Error("This assistant does not support attachments.");
|
|
70
|
+
return this.request(`/handoffs/${encodeURIComponent(handoffId)}/attachments/${encodeURIComponent(attachmentId)}?name=${encodeURIComponent(name)}`, {
|
|
71
|
+
method: "PUT", body: file, signal: options.signal, headers: { "content-type": file.type || "application/octet-stream" },
|
|
72
|
+
}, false);
|
|
73
|
+
}
|
|
74
|
+
finalize(handoffId, attachmentIds) {
|
|
75
|
+
return this.request(`/handoffs/${encodeURIComponent(handoffId)}/finalize`, { method: "POST", body: JSON.stringify({ attachmentIds }) });
|
|
76
|
+
}
|
|
77
|
+
status(handoffId) { return this.request(`/handoffs/${encodeURIComponent(handoffId)}`); }
|
|
78
|
+
cancel(handoffId) { return this.request(`/handoffs/${encodeURIComponent(handoffId)}`, { method: "DELETE" }); }
|
|
79
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -22,16 +22,10 @@ export declare function createAuthRequiredIntent(appId: string): AuthRequiredInt
|
|
|
22
22
|
* one (Hosty Shell: the assistant gateway, which it already mints tokens for).
|
|
23
23
|
*/
|
|
24
24
|
/**
|
|
25
|
-
* An embedded app
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
* entering a model that holds host shell is the same trust boundary that keeps MCP providers off by
|
|
30
|
-
* default, and an error message is exactly the shape a prompt injection arrives in. The operator
|
|
31
|
-
* reads it, with its source shown, and decides.
|
|
32
|
-
*
|
|
33
|
-
* Plain text only, and no reply. A structured payload would invite apps to smuggle shape the
|
|
34
|
-
* operator cannot read at a glance, and an answer would turn a hand-off into a channel.
|
|
25
|
+
* An embedded app handing text to the selected assistant through its embedder.
|
|
26
|
+
* The receiving assistant applies its own draft/immediate-start setting. Authentication
|
|
27
|
+
* and tool permissions still apply; the message does not prove prompt authorship.
|
|
28
|
+
* This small embedded channel carries plain text and has no execution-result reply.
|
|
35
29
|
*/
|
|
36
30
|
export declare const ASK_ASSISTANT_TYPE = "hosty:ask-assistant";
|
|
37
31
|
/**
|
|
@@ -49,7 +43,7 @@ export interface AskAssistantMessage {
|
|
|
49
43
|
text: string;
|
|
50
44
|
}
|
|
51
45
|
/**
|
|
52
|
-
* Asks the embedder to
|
|
46
|
+
* Asks the embedder to hand `text` to the operator's selected assistant.
|
|
53
47
|
*
|
|
54
48
|
* Safe to call unconditionally: standalone there is no embedder to hear it, and an embedder that
|
|
55
49
|
* does not offer an assistant simply ignores it. Returns whether the message could be posted at
|
package/dist/index.js
CHANGED
|
@@ -25,16 +25,10 @@ export function createAuthRequiredIntent(appId) {
|
|
|
25
25
|
* one (Hosty Shell: the assistant gateway, which it already mints tokens for).
|
|
26
26
|
*/
|
|
27
27
|
/**
|
|
28
|
-
* An embedded app
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* entering a model that holds host shell is the same trust boundary that keeps MCP providers off by
|
|
33
|
-
* default, and an error message is exactly the shape a prompt injection arrives in. The operator
|
|
34
|
-
* reads it, with its source shown, and decides.
|
|
35
|
-
*
|
|
36
|
-
* Plain text only, and no reply. A structured payload would invite apps to smuggle shape the
|
|
37
|
-
* operator cannot read at a glance, and an answer would turn a hand-off into a channel.
|
|
28
|
+
* An embedded app handing text to the selected assistant through its embedder.
|
|
29
|
+
* The receiving assistant applies its own draft/immediate-start setting. Authentication
|
|
30
|
+
* and tool permissions still apply; the message does not prove prompt authorship.
|
|
31
|
+
* This small embedded channel carries plain text and has no execution-result reply.
|
|
38
32
|
*/
|
|
39
33
|
export const ASK_ASSISTANT_TYPE = "hosty:ask-assistant";
|
|
40
34
|
/**
|
|
@@ -47,7 +41,7 @@ export const ATTENTION_TYPE = "hosty:assistant-attention";
|
|
|
47
41
|
/** Longer than a prompt fragment is a page dumping itself into someone's draft. */
|
|
48
42
|
export const ASK_ASSISTANT_MAX_CHARS = 4000;
|
|
49
43
|
/**
|
|
50
|
-
* Asks the embedder to
|
|
44
|
+
* Asks the embedder to hand `text` to the operator's selected assistant.
|
|
51
45
|
*
|
|
52
46
|
* Safe to call unconditionally: standalone there is no embedder to hear it, and an embedder that
|
|
53
47
|
* does not offer an assistant simply ignores it. Returns whether the message could be posted at
|
package/dist/install-react.js
CHANGED
|
@@ -64,7 +64,7 @@ export function InstallDialog({ client, source, onClose, onInstalled }) {
|
|
|
64
64
|
else
|
|
65
65
|
popup?.close();
|
|
66
66
|
};
|
|
67
|
-
return _jsxs("dialog", { ref: dialog, className: "hosty-install", "aria-labelledby": titleId, onCancel: event => { event.preventDefault(); onClose(); }, children: [_jsx("style", { children: styles }), _jsxs("header", { children: [_jsx("h2", { id: titleId, children: "Install app" }), _jsx("button", { type: "button", onClick: onClose, "aria-label": "Close installation", children: "\u00D7" })] }), _jsx("p", { children: "Prepare the installation here, then confirm it securely in Hosty Core." }), !source?.feedsUrl && !source?.manifestPath && _jsxs("form", { onSubmit: event => { event.preventDefault(); void review(); }, children: [_jsxs("label", { children: ["Manifest, app directory, or URL", _jsx("input", { value: manifestPath, onChange: event => { setManifestPath(event.target.value); flow.clearReview(); }, required: true, disabled: busy || frozen })] }), _jsx("button", { disabled: busy || frozen || !manifestPath.trim(), children: "Review" })] }), busy && _jsx("p", { role: "status", children: "Preparing installation\u2026" }), error && _jsxs("div", { role: "alert", children: [_jsx("p", { children: error }), !frozen && _jsx("button", { type: "button", disabled: busy, onClick: () => void review(), children: "Retry review" })] }), plan && _jsxs("form", { onSubmit: event => { event.preventDefault(); void submit(); }, children: [_jsxs("h3", { children: [plan.displayName, " ", _jsx("small", { children: plan.targetVersion })] }), plan.description && _jsx("p", { children: plan.description }), _jsxs("label", { children: ["Runtime", _jsx("select", { value: plan.targetRuntime, disabled: busy || frozen, onChange: event => void review(event.target.value), children: (plan.runtimeProfiles ?? [{ key: plan.targetRuntime, type: plan.targetRuntimeType }]).map(runtime => _jsxs("option", { value: runtime.key, children: [runtime.key, " (", runtime.type, ")"] }, runtime.key)) })] }), plan.targetRuntimeType === "localCommand" && _jsx("p", { className: "warning", children: "This runtime runs commands directly on your host, outside a container. Only install code you trust." }), plan.system && _jsx("p", { className: "warning", children: "This is a system app. Only host administrators can open it." }), !!plan.corePermissions?.length && _jsxs("section", { children: [_jsx("h3", { children: "Requested Core permissions" }), _jsx("ul", { children: plan.corePermissions.map(permission => _jsx("li", { children: permission }, permission)) })] }), plan.settings.map(setting => _jsxs("label", { children: [setting.label || setting.key, setting.required ? " (required)" : "", setting.type === "boolean" && !setting.secret
|
|
67
|
+
return _jsxs("dialog", { ref: dialog, className: "hosty-install", "aria-labelledby": titleId, onCancel: event => { event.preventDefault(); onClose(); }, children: [_jsx("style", { children: styles }), _jsxs("header", { children: [_jsx("h2", { id: titleId, children: "Install app" }), _jsx("button", { type: "button", onClick: onClose, "aria-label": "Close installation", children: "\u00D7" })] }), _jsx("p", { children: "Prepare the installation here, then confirm it securely in Hosty Core." }), !source?.feedsUrl && !source?.manifestPath && _jsxs("form", { onSubmit: event => { event.preventDefault(); void review(); }, children: [_jsxs("label", { children: ["Manifest, app directory, or URL", _jsx("input", { value: manifestPath, onChange: event => { setManifestPath(event.target.value); flow.clearReview(); }, required: true, disabled: busy || frozen })] }), _jsx("button", { disabled: busy || frozen || !manifestPath.trim(), children: "Review" })] }), busy && _jsx("p", { role: "status", children: "Preparing installation\u2026" }), error && _jsxs("div", { role: "alert", children: [_jsx("p", { children: error }), !frozen && _jsx("button", { type: "button", disabled: busy, onClick: () => void review(), children: "Retry review" })] }), plan && _jsxs("form", { onSubmit: event => { event.preventDefault(); void submit(); }, children: [_jsxs("h3", { children: [plan.displayName, " ", _jsx("small", { children: plan.targetVersion })] }), plan.description && _jsx("p", { children: plan.description }), _jsxs("label", { children: ["Runtime", _jsx("select", { value: plan.targetRuntime, disabled: busy || frozen, onChange: event => void review(event.target.value), children: (plan.runtimeProfiles ?? [{ key: plan.targetRuntime, type: plan.targetRuntimeType }]).map(runtime => _jsxs("option", { value: runtime.key, children: [runtime.key, " (", runtime.type, ")"] }, runtime.key)) })] }), plan.targetRuntimeType === "localCommand" && _jsx("p", { className: "warning", children: "This runtime runs commands directly on your host, outside a container. Only install code you trust." }), plan.system && _jsx("p", { className: "warning", children: "This is a system app. Only host administrators can open it." }), !!plan.requestedRoles?.length && _jsxs("section", { children: [_jsx("h3", { children: "Requested provider roles" }), _jsx("ul", { children: plan.requestedRoles.map(role => _jsx("li", { children: plan.roleDescriptions?.[role] ?? role }, role)) })] }), !!plan.corePermissions?.length && _jsxs("section", { children: [_jsx("h3", { children: "Requested Core permissions" }), _jsx("ul", { children: plan.corePermissions.map(permission => _jsx("li", { children: plan.permissionDescriptions?.[permission] ?? permission }, permission)) })] }), plan.settings.map(setting => _jsxs("label", { children: [setting.label || setting.key, setting.required ? " (required)" : "", setting.type === "boolean" && !setting.secret
|
|
68
68
|
? _jsx("input", { type: "checkbox", checked: ["true", "1", "yes", "on", "enabled"].includes((settings[setting.key] ?? "").toLowerCase()), disabled: busy || frozen, onChange: event => setSettings(current => ({ ...current, [setting.key]: String(event.target.checked) })) })
|
|
69
69
|
: setting.type === "select" && setting.options?.length && !setting.secret
|
|
70
70
|
? _jsxs("select", { value: settings[setting.key] ?? "", disabled: busy || frozen, onChange: event => setSettings(current => ({ ...current, [setting.key]: event.target.value })), children: [_jsx("option", { value: "", children: "Select a value" }), setting.options.map(option => _jsx("option", { value: option.value, children: option.label }, option.value))] })
|
package/dist/install.d.ts
CHANGED
|
@@ -26,6 +26,9 @@ export interface InstallPlan {
|
|
|
26
26
|
defaultAutostart?: boolean;
|
|
27
27
|
system?: boolean;
|
|
28
28
|
corePermissions?: string[];
|
|
29
|
+
requestedRoles?: string[];
|
|
30
|
+
permissionDescriptions?: Record<string, string>;
|
|
31
|
+
roleDescriptions?: Record<string, string>;
|
|
29
32
|
runtimeProfiles?: {
|
|
30
33
|
key: string;
|
|
31
34
|
type: string;
|
package/dist/server.js
CHANGED
|
@@ -552,6 +552,6 @@ async function readJsonOrThrow(response) {
|
|
|
552
552
|
}
|
|
553
553
|
}
|
|
554
554
|
// Delegated tokens live in their own entry ("./delegated") because that module must stay
|
|
555
|
-
// importable from plain Node services (the
|
|
555
|
+
// importable from plain Node services (the Harness system app) — this file's "server-only"
|
|
556
556
|
// import would throw there. Re-exported here so Next apps keep one import surface.
|
|
557
557
|
export { validateDelegatedToken, } from "./delegated.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hosty-sdk/app",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.1",
|
|
4
4
|
"description": "Hosty app auth: session state machine, recovery bridge, Core revalidation, and the embedder responder.",
|
|
5
5
|
"license": "AGPL-3.0-only",
|
|
6
6
|
"type": "module",
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
"./install/server": {
|
|
49
49
|
"types": "./dist/install-server.d.ts",
|
|
50
50
|
"default": "./dist/install-server.js"
|
|
51
|
+
},
|
|
52
|
+
"./assistant": {
|
|
53
|
+
"types": "./dist/assistant.d.ts",
|
|
54
|
+
"default": "./dist/assistant.js"
|
|
51
55
|
}
|
|
52
56
|
},
|
|
53
57
|
"scripts": {
|
|
@@ -58,7 +62,7 @@
|
|
|
58
62
|
"server-only": "^0.0.1"
|
|
59
63
|
},
|
|
60
64
|
"peerDependencies": {
|
|
61
|
-
"next": "^16.3.
|
|
65
|
+
"next": "^16.3.5",
|
|
62
66
|
"react": "^19.2.8"
|
|
63
67
|
},
|
|
64
68
|
"peerDependenciesMeta": {
|
|
@@ -70,10 +74,10 @@
|
|
|
70
74
|
}
|
|
71
75
|
},
|
|
72
76
|
"devDependencies": {
|
|
73
|
-
"@types/node": "^24.13.
|
|
77
|
+
"@types/node": "^24.13.5",
|
|
74
78
|
"@types/react": "^19.3.0",
|
|
75
79
|
"typescript": "^6",
|
|
76
|
-
"vitest": "^5.0.
|
|
80
|
+
"vitest": "^5.0.1"
|
|
77
81
|
},
|
|
78
82
|
"repository": {
|
|
79
83
|
"type": "git",
|