@hyperdrive.bot/paseo-extension-sdk 0.3.3
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 +51 -0
- package/dist/activation.d.ts +22 -0
- package/dist/activation.js +2 -0
- package/dist/host-api.d.ts +73 -0
- package/dist/host-api.js +9 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/manifest.d.ts +103 -0
- package/dist/manifest.js +66 -0
- package/dist/rpc.d.ts +39 -0
- package/dist/rpc.js +80 -0
- package/package.json +38 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Marcelo Marra
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# @hyperdrive.bot/paseo-extension-sdk
|
|
2
|
+
|
|
3
|
+
**MIT-licensed** SDK for building [Hyperpaseo](https://paseo.sh) extensions.
|
|
4
|
+
|
|
5
|
+
This package is the **contract** between an extension and the host app — a
|
|
6
|
+
VS Code–style plugin model. It contains only interfaces, the manifest schema,
|
|
7
|
+
and a thin RPC client. It does **not** import any host (AGPL) code.
|
|
8
|
+
|
|
9
|
+
## Why this is a separate package (the licensing boundary)
|
|
10
|
+
|
|
11
|
+
The Hyperpaseo desktop app is **AGPL-3.0**. Extensions are **not** derivative
|
|
12
|
+
works of it because they run **arms-length**:
|
|
13
|
+
|
|
14
|
+
- extension logic runs in a separate **extension-host process** (not in the app),
|
|
15
|
+
- extension UI runs in a **sandboxed webview**,
|
|
16
|
+
- both communicate with the host only over the **RPC contract** defined here.
|
|
17
|
+
|
|
18
|
+
An extension depends on `@hyperdrive.bot/paseo-extension-sdk` (MIT) and the wire protocol
|
|
19
|
+
— never on the app's source. Therefore an extension may be licensed however its
|
|
20
|
+
author wants, **including closed-source and paid**. (Same boundary VS Code uses
|
|
21
|
+
between Code-OSS and its extension ecosystem.)
|
|
22
|
+
|
|
23
|
+
> Do NOT import host internals into an extension. The moment an extension links
|
|
24
|
+
> the AGPL app in-process, it becomes a derivative work and must be AGPL.
|
|
25
|
+
> Talk to the host only through the `HostApi` defined here.
|
|
26
|
+
|
|
27
|
+
## Anatomy of an extension
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
my-extension/
|
|
31
|
+
package.json # name, version
|
|
32
|
+
hyperpaseo.json # the manifest (contributions)
|
|
33
|
+
dist/extension.js # runs in the extension-host (Node) — exports activate()
|
|
34
|
+
ui/index.html # optional sandboxed webview UI
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
// extension.ts — runs in the extension-host process
|
|
39
|
+
import type { ExtensionContext } from "@hyperdrive.bot/paseo-extension-sdk";
|
|
40
|
+
|
|
41
|
+
export async function activate(ctx: ExtensionContext): Promise<void> {
|
|
42
|
+
ctx.registerCommand("hello.ping", async () => {
|
|
43
|
+
const agents = await ctx.host.agents.list();
|
|
44
|
+
await ctx.host.ui.showNotification({ body: `You have ${agents.length} agents.` });
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function deactivate(): void {}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
See `src/` for the full surface: `manifest`, `host-api`, `activation`, `rpc`.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { Disposable, HostApi } from "./host-api.js";
|
|
2
|
+
/**
|
|
3
|
+
* The context handed to an extension's `activate()` when the extension-host
|
|
4
|
+
* loads it. `host` is the RPC-backed capability surface; `registerCommand`
|
|
5
|
+
* wires a handler for a command declared in the manifest's `contributes.commands`.
|
|
6
|
+
*
|
|
7
|
+
* Push any Disposable you create into `subscriptions`; the host disposes them
|
|
8
|
+
* all on `deactivate()` / unload.
|
|
9
|
+
*/
|
|
10
|
+
export interface ExtensionContext {
|
|
11
|
+
host: HostApi;
|
|
12
|
+
subscriptions: Disposable[];
|
|
13
|
+
registerCommand(id: string, handler: (...args: unknown[]) => unknown | Promise<unknown>): Disposable;
|
|
14
|
+
}
|
|
15
|
+
export type ActivateFn = (context: ExtensionContext) => void | Promise<void>;
|
|
16
|
+
export type DeactivateFn = () => void | Promise<void>;
|
|
17
|
+
/** The shape the extension-host expects from an extension's `main` module. */
|
|
18
|
+
export interface ExtensionModule {
|
|
19
|
+
activate: ActivateFn;
|
|
20
|
+
deactivate?: DeactivateFn;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=activation.d.ts.map
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HostApi is everything an extension may ask of the host, over RPC.
|
|
3
|
+
*
|
|
4
|
+
* These are CLEAN-ROOM shapes — deliberately NOT imported from the AGPL app.
|
|
5
|
+
* They are a stable, minimal projection of host capabilities. The host adapts
|
|
6
|
+
* its internal types to these at the gateway; the extension only ever sees this.
|
|
7
|
+
*/
|
|
8
|
+
export interface Disposable {
|
|
9
|
+
dispose(): void;
|
|
10
|
+
}
|
|
11
|
+
export type AgentStatus = "running" | "idle" | "waiting" | "error" | "archived" | "unknown";
|
|
12
|
+
export interface AgentSummary {
|
|
13
|
+
id: string;
|
|
14
|
+
title: string | null;
|
|
15
|
+
status: AgentStatus;
|
|
16
|
+
serverId: string;
|
|
17
|
+
cwd: string | null;
|
|
18
|
+
/** ISO-8601 timestamp, or null if unknown. */
|
|
19
|
+
lastActivityAt: string | null;
|
|
20
|
+
}
|
|
21
|
+
export interface AgentDetail extends AgentSummary {
|
|
22
|
+
provider: string | null;
|
|
23
|
+
pendingPermissionCount: number;
|
|
24
|
+
}
|
|
25
|
+
export interface NotificationOptions {
|
|
26
|
+
title?: string;
|
|
27
|
+
body: string;
|
|
28
|
+
}
|
|
29
|
+
export interface HostFetchInit {
|
|
30
|
+
method?: string;
|
|
31
|
+
headers?: Record<string, string>;
|
|
32
|
+
body?: string;
|
|
33
|
+
}
|
|
34
|
+
export interface HostFetchResponse {
|
|
35
|
+
status: number;
|
|
36
|
+
headers: Record<string, string>;
|
|
37
|
+
body: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The capability surface. v0 wraps a curated subset of the host's DaemonClient
|
|
41
|
+
* plus UI affordances. `net.fetch` is the escape hatch for an extension's OWN
|
|
42
|
+
* (possibly paid) backend — calls leave the host arms-length.
|
|
43
|
+
*/
|
|
44
|
+
export interface HostApi {
|
|
45
|
+
agents: {
|
|
46
|
+
list(): Promise<AgentSummary[]>;
|
|
47
|
+
get(id: string): Promise<AgentDetail | null>;
|
|
48
|
+
/** Subscribe to agent-list status changes. Returns a Disposable. */
|
|
49
|
+
onStatus(listener: (agents: AgentSummary[]) => void): Disposable;
|
|
50
|
+
archive(id: string): Promise<void>;
|
|
51
|
+
};
|
|
52
|
+
commands: {
|
|
53
|
+
/** Invoke any registered command (host or another extension's). */
|
|
54
|
+
execute(id: string, ...args: unknown[]): Promise<unknown>;
|
|
55
|
+
};
|
|
56
|
+
ui: {
|
|
57
|
+
showNotification(options: NotificationOptions): Promise<void>;
|
|
58
|
+
/** Open a contributed view (webview) by id. */
|
|
59
|
+
openView(viewId: string): Promise<void>;
|
|
60
|
+
/** Open a workspace-tab panel by kind, with optional props. */
|
|
61
|
+
openPanel(kind: string, props?: Record<string, unknown>): Promise<void>;
|
|
62
|
+
};
|
|
63
|
+
storage: {
|
|
64
|
+
/** Per-extension scoped key/value storage (host-persisted). */
|
|
65
|
+
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
66
|
+
set(key: string, value: unknown): Promise<void>;
|
|
67
|
+
};
|
|
68
|
+
net: {
|
|
69
|
+
/** Arms-length network call — for the extension's own backend. */
|
|
70
|
+
fetch(url: string, init?: HostFetchInit): Promise<HostFetchResponse>;
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=host-api.d.ts.map
|
package/dist/host-api.js
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HostApi is everything an extension may ask of the host, over RPC.
|
|
3
|
+
*
|
|
4
|
+
* These are CLEAN-ROOM shapes — deliberately NOT imported from the AGPL app.
|
|
5
|
+
* They are a stable, minimal projection of host capabilities. The host adapts
|
|
6
|
+
* its internal types to these at the gateway; the extension only ever sees this.
|
|
7
|
+
*/
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=host-api.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The extension manifest (`hyperpaseo.json`). Pure declaration — the host reads
|
|
4
|
+
* this to know what an extension contributes WITHOUT executing any extension
|
|
5
|
+
* code. This is the arms-length seam: contributions are data, not linked code.
|
|
6
|
+
*/
|
|
7
|
+
export declare const CommandContribution: z.ZodObject<{
|
|
8
|
+
id: z.ZodString;
|
|
9
|
+
title: z.ZodString;
|
|
10
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
11
|
+
}, z.core.$strip>;
|
|
12
|
+
export type CommandContribution = z.infer<typeof CommandContribution>;
|
|
13
|
+
export declare const SidebarItemContribution: z.ZodObject<{
|
|
14
|
+
id: z.ZodString;
|
|
15
|
+
title: z.ZodString;
|
|
16
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
17
|
+
command: z.ZodOptional<z.ZodString>;
|
|
18
|
+
view: z.ZodOptional<z.ZodString>;
|
|
19
|
+
order: z.ZodDefault<z.ZodNumber>;
|
|
20
|
+
}, z.core.$strip>;
|
|
21
|
+
export type SidebarItemContribution = z.infer<typeof SidebarItemContribution>;
|
|
22
|
+
export declare const ViewContribution: z.ZodObject<{
|
|
23
|
+
id: z.ZodString;
|
|
24
|
+
title: z.ZodString;
|
|
25
|
+
location: z.ZodDefault<z.ZodEnum<{
|
|
26
|
+
sidebar: "sidebar";
|
|
27
|
+
panel: "panel";
|
|
28
|
+
}>>;
|
|
29
|
+
ui: z.ZodString;
|
|
30
|
+
}, z.core.$strip>;
|
|
31
|
+
export type ViewContribution = z.infer<typeof ViewContribution>;
|
|
32
|
+
export declare const Contributes: z.ZodObject<{
|
|
33
|
+
commands: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
34
|
+
id: z.ZodString;
|
|
35
|
+
title: z.ZodString;
|
|
36
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
37
|
+
}, z.core.$strip>>>;
|
|
38
|
+
sidebarItems: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
39
|
+
id: z.ZodString;
|
|
40
|
+
title: z.ZodString;
|
|
41
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
42
|
+
command: z.ZodOptional<z.ZodString>;
|
|
43
|
+
view: z.ZodOptional<z.ZodString>;
|
|
44
|
+
order: z.ZodDefault<z.ZodNumber>;
|
|
45
|
+
}, z.core.$strip>>>;
|
|
46
|
+
views: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
47
|
+
id: z.ZodString;
|
|
48
|
+
title: z.ZodString;
|
|
49
|
+
location: z.ZodDefault<z.ZodEnum<{
|
|
50
|
+
sidebar: "sidebar";
|
|
51
|
+
panel: "panel";
|
|
52
|
+
}>>;
|
|
53
|
+
ui: z.ZodString;
|
|
54
|
+
}, z.core.$strip>>>;
|
|
55
|
+
}, z.core.$strip>;
|
|
56
|
+
export type Contributes = z.infer<typeof Contributes>;
|
|
57
|
+
export declare const ExtensionManifest: z.ZodObject<{
|
|
58
|
+
id: z.ZodString;
|
|
59
|
+
name: z.ZodString;
|
|
60
|
+
version: z.ZodString;
|
|
61
|
+
publisher: z.ZodOptional<z.ZodString>;
|
|
62
|
+
description: z.ZodOptional<z.ZodString>;
|
|
63
|
+
engines: z.ZodObject<{
|
|
64
|
+
hyperpaseo: z.ZodString;
|
|
65
|
+
}, z.core.$strip>;
|
|
66
|
+
main: z.ZodOptional<z.ZodString>;
|
|
67
|
+
contributes: z.ZodDefault<z.ZodObject<{
|
|
68
|
+
commands: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
69
|
+
id: z.ZodString;
|
|
70
|
+
title: z.ZodString;
|
|
71
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
72
|
+
}, z.core.$strip>>>;
|
|
73
|
+
sidebarItems: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
74
|
+
id: z.ZodString;
|
|
75
|
+
title: z.ZodString;
|
|
76
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
77
|
+
command: z.ZodOptional<z.ZodString>;
|
|
78
|
+
view: z.ZodOptional<z.ZodString>;
|
|
79
|
+
order: z.ZodDefault<z.ZodNumber>;
|
|
80
|
+
}, z.core.$strip>>>;
|
|
81
|
+
views: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
82
|
+
id: z.ZodString;
|
|
83
|
+
title: z.ZodString;
|
|
84
|
+
location: z.ZodDefault<z.ZodEnum<{
|
|
85
|
+
sidebar: "sidebar";
|
|
86
|
+
panel: "panel";
|
|
87
|
+
}>>;
|
|
88
|
+
ui: z.ZodString;
|
|
89
|
+
}, z.core.$strip>>>;
|
|
90
|
+
}, z.core.$strip>>;
|
|
91
|
+
}, z.core.$strip>;
|
|
92
|
+
export type ExtensionManifest = z.infer<typeof ExtensionManifest>;
|
|
93
|
+
/** Parse + validate a raw manifest object. Throws ZodError on invalid input. */
|
|
94
|
+
export declare function parseManifest(raw: unknown): ExtensionManifest;
|
|
95
|
+
/** Safe variant — returns a typed result instead of throwing. */
|
|
96
|
+
export declare function safeParseManifest(raw: unknown): {
|
|
97
|
+
ok: true;
|
|
98
|
+
manifest: ExtensionManifest;
|
|
99
|
+
} | {
|
|
100
|
+
ok: false;
|
|
101
|
+
error: string;
|
|
102
|
+
};
|
|
103
|
+
//# sourceMappingURL=manifest.d.ts.map
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* The extension manifest (`hyperpaseo.json`). Pure declaration — the host reads
|
|
4
|
+
* this to know what an extension contributes WITHOUT executing any extension
|
|
5
|
+
* code. This is the arms-length seam: contributions are data, not linked code.
|
|
6
|
+
*/
|
|
7
|
+
export const CommandContribution = z.object({
|
|
8
|
+
/** Stable command id, namespaced by extension, e.g. "meetings.openInbox". */
|
|
9
|
+
id: z.string(),
|
|
10
|
+
/** Human-readable label shown in the command palette. */
|
|
11
|
+
title: z.string(),
|
|
12
|
+
/** Optional host icon token (e.g. "plus", "calendar"). */
|
|
13
|
+
icon: z.string().optional(),
|
|
14
|
+
});
|
|
15
|
+
export const SidebarItemContribution = z.object({
|
|
16
|
+
id: z.string(),
|
|
17
|
+
title: z.string(),
|
|
18
|
+
icon: z.string().optional(),
|
|
19
|
+
/** Selecting the item runs this command id... */
|
|
20
|
+
command: z.string().optional(),
|
|
21
|
+
/** ...or opens this view id (a webview). Exactly one of command|view. */
|
|
22
|
+
view: z.string().optional(),
|
|
23
|
+
/** Lower sorts first; host items occupy 0..99, extensions default to 100+. */
|
|
24
|
+
order: z.number().default(100),
|
|
25
|
+
});
|
|
26
|
+
export const ViewContribution = z.object({
|
|
27
|
+
id: z.string(),
|
|
28
|
+
title: z.string(),
|
|
29
|
+
/** Where the view mounts. */
|
|
30
|
+
location: z.enum(["sidebar", "panel"]).default("panel"),
|
|
31
|
+
/** Relative path to the sandboxed webview HTML entry inside the extension. */
|
|
32
|
+
ui: z.string(),
|
|
33
|
+
});
|
|
34
|
+
export const Contributes = z.object({
|
|
35
|
+
commands: z.array(CommandContribution).default([]),
|
|
36
|
+
sidebarItems: z.array(SidebarItemContribution).default([]),
|
|
37
|
+
views: z.array(ViewContribution).default([]),
|
|
38
|
+
});
|
|
39
|
+
export const ExtensionManifest = z.object({
|
|
40
|
+
/** Reverse-DNS unique id, e.g. "com.hyperpaseo.meetings". */
|
|
41
|
+
id: z.string(),
|
|
42
|
+
name: z.string(),
|
|
43
|
+
version: z.string(),
|
|
44
|
+
publisher: z.string().optional(),
|
|
45
|
+
description: z.string().optional(),
|
|
46
|
+
/** Host compatibility range, semver against the app version. */
|
|
47
|
+
engines: z.object({ hyperpaseo: z.string() }),
|
|
48
|
+
/**
|
|
49
|
+
* Entry that runs in the extension-host (Node). Exports activate()/deactivate().
|
|
50
|
+
* Omit for purely-declarative or pure-webview extensions.
|
|
51
|
+
*/
|
|
52
|
+
main: z.string().optional(),
|
|
53
|
+
contributes: Contributes.default({ commands: [], sidebarItems: [], views: [] }),
|
|
54
|
+
});
|
|
55
|
+
/** Parse + validate a raw manifest object. Throws ZodError on invalid input. */
|
|
56
|
+
export function parseManifest(raw) {
|
|
57
|
+
return ExtensionManifest.parse(raw);
|
|
58
|
+
}
|
|
59
|
+
/** Safe variant — returns a typed result instead of throwing. */
|
|
60
|
+
export function safeParseManifest(raw) {
|
|
61
|
+
const result = ExtensionManifest.safeParse(raw);
|
|
62
|
+
if (result.success)
|
|
63
|
+
return { ok: true, manifest: result.data };
|
|
64
|
+
return { ok: false, error: result.error.message };
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=manifest.js.map
|
package/dist/rpc.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { Disposable, HostApi } from "./host-api.js";
|
|
2
|
+
/**
|
|
3
|
+
* Transport-agnostic RPC. The same wire contract works over:
|
|
4
|
+
* - extension-host process stdio (Node) — extension logic
|
|
5
|
+
* - webview postMessage (sandboxed iframe) — extension UI
|
|
6
|
+
*
|
|
7
|
+
* The host implements the other end of these messages against its DaemonClient.
|
|
8
|
+
*/
|
|
9
|
+
export interface RpcTransport {
|
|
10
|
+
post(message: unknown): void;
|
|
11
|
+
/** Register an inbound-message handler. Returns a Disposable. */
|
|
12
|
+
subscribe(handler: (message: unknown) => void): Disposable;
|
|
13
|
+
}
|
|
14
|
+
export interface CallMessage {
|
|
15
|
+
kind: "call";
|
|
16
|
+
id: number;
|
|
17
|
+
path: string[];
|
|
18
|
+
args: unknown[];
|
|
19
|
+
}
|
|
20
|
+
export interface ResultMessage {
|
|
21
|
+
kind: "result";
|
|
22
|
+
id: number;
|
|
23
|
+
ok: boolean;
|
|
24
|
+
value?: unknown;
|
|
25
|
+
error?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface EventMessage {
|
|
28
|
+
kind: "event";
|
|
29
|
+
channel: string;
|
|
30
|
+
payload: unknown;
|
|
31
|
+
}
|
|
32
|
+
export type HostInboundMessage = ResultMessage | EventMessage;
|
|
33
|
+
/**
|
|
34
|
+
* Build a HostApi proxy backed by an RpcTransport. Outbound method calls become
|
|
35
|
+
* `call` messages; the host replies with `result`; `onStatus` subscribes to an
|
|
36
|
+
* `event` channel. No host code is linked — only the wire shapes above.
|
|
37
|
+
*/
|
|
38
|
+
export declare function createHostApi(transport: RpcTransport): HostApi;
|
|
39
|
+
//# sourceMappingURL=rpc.d.ts.map
|
package/dist/rpc.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
const SUBSCRIBE_PATH = "__subscribe__";
|
|
2
|
+
/**
|
|
3
|
+
* Build a HostApi proxy backed by an RpcTransport. Outbound method calls become
|
|
4
|
+
* `call` messages; the host replies with `result`; `onStatus` subscribes to an
|
|
5
|
+
* `event` channel. No host code is linked — only the wire shapes above.
|
|
6
|
+
*/
|
|
7
|
+
export function createHostApi(transport) {
|
|
8
|
+
let nextId = 1;
|
|
9
|
+
const pending = new Map();
|
|
10
|
+
const channels = new Map();
|
|
11
|
+
transport.subscribe((raw) => {
|
|
12
|
+
const message = raw;
|
|
13
|
+
if (!message || typeof message !== "object")
|
|
14
|
+
return;
|
|
15
|
+
if (message.kind === "result") {
|
|
16
|
+
const pendingCall = pending.get(message.id);
|
|
17
|
+
if (!pendingCall)
|
|
18
|
+
return;
|
|
19
|
+
pending.delete(message.id);
|
|
20
|
+
if (message.ok)
|
|
21
|
+
pendingCall.resolve(message.value);
|
|
22
|
+
else
|
|
23
|
+
pendingCall.reject(new Error(message.error ?? "host call failed"));
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
if (message.kind === "event") {
|
|
27
|
+
const listeners = channels.get(message.channel);
|
|
28
|
+
if (!listeners)
|
|
29
|
+
return;
|
|
30
|
+
for (const listener of listeners)
|
|
31
|
+
listener(message.payload);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
function call(path, args) {
|
|
35
|
+
const id = nextId++;
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
pending.set(id, { resolve, reject });
|
|
38
|
+
const message = { kind: "call", id, path, args };
|
|
39
|
+
transport.post(message);
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
function subscribe(channel, listener) {
|
|
43
|
+
let listeners = channels.get(channel);
|
|
44
|
+
if (!listeners) {
|
|
45
|
+
listeners = new Set();
|
|
46
|
+
channels.set(channel, listeners);
|
|
47
|
+
}
|
|
48
|
+
listeners.add(listener);
|
|
49
|
+
void call([SUBSCRIBE_PATH], [channel]);
|
|
50
|
+
return {
|
|
51
|
+
dispose: () => {
|
|
52
|
+
listeners?.delete(listener);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
agents: {
|
|
58
|
+
list: () => call(["agents", "list"], []),
|
|
59
|
+
get: (id) => call(["agents", "get"], [id]),
|
|
60
|
+
onStatus: (listener) => subscribe("agents.status", (payload) => listener(payload)),
|
|
61
|
+
archive: (id) => call(["agents", "archive"], [id]),
|
|
62
|
+
},
|
|
63
|
+
commands: {
|
|
64
|
+
execute: (id, ...args) => call(["commands", "execute"], [id, ...args]),
|
|
65
|
+
},
|
|
66
|
+
ui: {
|
|
67
|
+
showNotification: (options) => call(["ui", "showNotification"], [options]),
|
|
68
|
+
openView: (viewId) => call(["ui", "openView"], [viewId]),
|
|
69
|
+
openPanel: (kind, props) => call(["ui", "openPanel"], [kind, props]),
|
|
70
|
+
},
|
|
71
|
+
storage: {
|
|
72
|
+
get: (key) => call(["storage", "get"], [key]),
|
|
73
|
+
set: (key, value) => call(["storage", "set"], [key, value]),
|
|
74
|
+
},
|
|
75
|
+
net: {
|
|
76
|
+
fetch: (url, init) => call(["net", "fetch"], [url, init]),
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=rpc.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@hyperdrive.bot/paseo-extension-sdk",
|
|
3
|
+
"version": "0.3.3",
|
|
4
|
+
"description": "MIT SDK for building Hyperpaseo extensions. Extensions run arms-length (separate extension-host process / sandboxed webview) and talk to the host over RPC — they never link the AGPL app.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"!dist/**/*.map",
|
|
9
|
+
"README.md",
|
|
10
|
+
"LICENSE"
|
|
11
|
+
],
|
|
12
|
+
"type": "module",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"./*": {
|
|
19
|
+
"types": "./dist/*.d.ts",
|
|
20
|
+
"default": "./dist/*.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"publishConfig": {
|
|
24
|
+
"access": "public"
|
|
25
|
+
},
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
|
28
|
+
"prepack": "npm run build",
|
|
29
|
+
"typecheck": "tsc --noEmit"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"zod": "^4.4.3"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^20.9.0",
|
|
36
|
+
"typescript": "^5.2.2"
|
|
37
|
+
}
|
|
38
|
+
}
|