@intentic/extension-api 1.247.0 → 1.249.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/dist/api.d.ts +9 -1
- package/dist/api.d.ts.map +1 -1
- package/dist/background.d.ts.map +1 -1
- package/dist/background.js.map +1 -1
- package/dist/diff.d.ts.map +1 -1
- package/dist/facts.d.ts.map +1 -1
- package/dist/host.d.ts.map +1 -1
- package/dist/host.js.map +1 -1
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js.map +1 -1
- package/dist/route.d.ts.map +1 -1
- package/dist/route.js.map +1 -1
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/stream.d.ts.map +1 -1
- package/dist/stream.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +3 -3
- package/src/api.ts +154 -384
- package/src/background.ts +25 -127
- package/src/diff.ts +16 -47
- package/src/facts.ts +9 -22
- package/src/host.ts +3 -13
- package/src/protocol.ts +3 -15
- package/src/route.ts +6 -14
- package/src/scope.ts +12 -73
- package/src/server.ts +14 -39
- package/src/stream.ts +7 -12
- package/src/surface.json +84 -0
- package/src/version.ts +6 -1
package/src/server.ts
CHANGED
|
@@ -1,50 +1,27 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* separate supervised node process shared by every enabled extension with a backend, imports the bundle and
|
|
5
|
-
* calls it. A separate process rather than the daemon itself because loaded code can never be unloaded: the
|
|
6
|
-
* off switch, an upgrade to a new sha and a live-edited workspace extension all require the process holding
|
|
7
|
-
* the old code to die, and that process must never be the daemon (chat, terminals and file sync live there).
|
|
8
|
-
* A shared process rather than one per extension because the trust model is full trust (install is owner-only
|
|
9
|
-
* and sha-pinned), isolation between extensions would buy robustness nobody is billed for.
|
|
10
|
-
*
|
|
11
|
-
* Full trust is also why this surface is deliberately small. The backend runs in the sandbox container as the
|
|
12
|
-
* same user the daemon does, so the workspace is reachable with plain `node:fs`, the api hands over PATHS,
|
|
13
|
-
* not a file service. What it does mediate is the two things a path cannot carry: the extension's route
|
|
14
|
-
* namespace (mount), and its reach into the daemon's own routes (daemon.*, gated by the manifest's
|
|
15
|
-
* `permissions.daemon`, the daemon refuses undeclared routes, same grammar and same honesty rule as the UI
|
|
16
|
-
* half's `permissions.sandbox`). */
|
|
1
|
+
// Backend counterpart to `IntenticApi` (api.ts); a manifest `server` bundle's `activateServer` runs in a node process
|
|
2
|
+
// shared by every enabled extension, separate from the daemon. Mediates only the extension's route namespace (mount)
|
|
3
|
+
// and its reach into daemon routes (`daemon.*`, gated by `permissions.daemon`).
|
|
17
4
|
|
|
18
|
-
// One request into this extension's
|
|
19
|
-
//
|
|
20
|
-
// `undefined` means "not mine": the host answers 404 without the extension having to speak HTTP for it.
|
|
5
|
+
// One request into this extension's `/x/<id>` namespace, prefix already stripped. Return `undefined` for "not mine":
|
|
6
|
+
// the host answers 404.
|
|
21
7
|
export type BackendRouteHandler = (request: Request) => Promise<Response | undefined>;
|
|
22
8
|
|
|
23
9
|
export interface ExtensionServerApi {
|
|
24
|
-
// The host's @intentic/extension-api version,
|
|
10
|
+
// The host's @intentic/extension-api version, checked against `engines.intentic`.
|
|
25
11
|
readonly apiVersion: string;
|
|
26
|
-
//
|
|
27
|
-
// means no file service in between. Durable state belongs in workspace files (the same rule the UI half
|
|
28
|
-
// lives by): it survives restarts, is shared across browsers, and the agent editing it out-of-band is the
|
|
29
|
-
// product.
|
|
12
|
+
// Absolute workspace root; the backend reads and writes it directly via node's `fs`, no file service in between.
|
|
30
13
|
readonly workspaceRoot: string;
|
|
31
14
|
// This extension's own checkout (absolute), where its bundled assets sit.
|
|
32
15
|
readonly extensionDir: string;
|
|
33
16
|
// A line in the daemon's log, attributed to this extension.
|
|
34
17
|
readonly log: (message: string) => void;
|
|
35
18
|
readonly routes: {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
* unauthenticated request and never sees a credential. One handler per extension: the extension owns
|
|
39
|
-
* its whole namespace, and how it routes inside it (an oRPC handler over its own contract, a plain
|
|
40
|
-
* switch) is its own business. A second mount replaces the first. */
|
|
19
|
+
// Serves this extension's route namespace; the daemon proxies /x/<id>/* here through its ordinary auth. A
|
|
20
|
+
// second mount replaces the first.
|
|
41
21
|
mount(handler: BackendRouteHandler): void;
|
|
42
22
|
};
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
* `permissions.daemon` allowlist, so a backend's reach into the core is declared, reviewable and refusable
|
|
46
|
-
* exactly like the UI half's. The extension's own namespace needs no declaration, but there is also no
|
|
47
|
-
* reason to dial yourself over HTTP. */
|
|
23
|
+
// Authenticated transport to the daemon's own routes; every call is checked against the manifest's
|
|
24
|
+
// `permissions.daemon` allowlist.
|
|
48
25
|
readonly daemon: {
|
|
49
26
|
request(path: string, init?: RequestInit): Promise<Response>;
|
|
50
27
|
json<T>(path: string, init?: RequestInit): Promise<T>;
|
|
@@ -52,14 +29,12 @@ export interface ExtensionServerApi {
|
|
|
52
29
|
}
|
|
53
30
|
|
|
54
31
|
export interface ExtensionServerContext {
|
|
55
|
-
//
|
|
56
|
-
// extension, publisher.name otherwise; the same id the UI half sees as ExtensionSummary.id).
|
|
32
|
+
// This extension's routing id, its /x/<id> namespace segment.
|
|
57
33
|
readonly extensionId: string;
|
|
58
34
|
}
|
|
59
35
|
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
-
// process ending, which is the one teardown that cannot leak.
|
|
36
|
+
// Shape of the manifest `server` bundle's default (or named) export; `activateServer` runs once per backend-host start.
|
|
37
|
+
// No deactivate: retirement is the host process ending.
|
|
63
38
|
export interface ExtensionServerModule {
|
|
64
39
|
activateServer(api: ExtensionServerApi, context: ExtensionServerContext): void | Promise<void>;
|
|
65
40
|
}
|
package/src/stream.ts
CHANGED
|
@@ -1,15 +1,11 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
* so consuming a streamed apply/plan/provision means reframing + JSON-parsing. Pure (ReadableStream in, async
|
|
4
|
-
* records out), no deps, extensions bundle it; the shim path never touches it. */
|
|
1
|
+
// Reads a daemon SSE/ndjson stream: reframes blank-line-separated `data: <JSON>` frames and parses each. Pure
|
|
2
|
+
// (ReadableStream in, async records out), no deps.
|
|
5
3
|
|
|
6
|
-
// A
|
|
7
|
-
// forever, hanging the consumer. A live daemon heartbeats (≤1s) over the stream, so no bytes for this long means
|
|
8
|
-
// the connection is dead: cancel the reader and end the generator instead of waiting indefinitely.
|
|
4
|
+
// A live daemon heartbeats over the stream at least this often; no bytes for this long means the connection is dead.
|
|
9
5
|
const SSE_IDLE_MS = 120_000;
|
|
10
6
|
|
|
11
|
-
// Yields each raw SSE frame
|
|
12
|
-
//
|
|
7
|
+
// Yields each raw SSE frame, reassembling frames split across chunks; ends (cancelling the reader) after SSE_IDLE_MS of
|
|
8
|
+
// silence.
|
|
13
9
|
async function* sseFrames(body: ReadableStream<Uint8Array>): AsyncGenerator<string> {
|
|
14
10
|
const reader = body.getReader();
|
|
15
11
|
const decoder = new TextDecoder();
|
|
@@ -58,9 +54,8 @@ const dataOf = (frame: string): unknown => {
|
|
|
58
54
|
}
|
|
59
55
|
};
|
|
60
56
|
|
|
61
|
-
// Reads a daemon stream as parsed ndjson records. An `event: error` frame
|
|
62
|
-
//
|
|
63
|
-
// own error line; malformed frames are skipped.
|
|
57
|
+
// Reads a daemon stream as parsed ndjson records. An `event: error` frame becomes a `{ kind: "error", message }`
|
|
58
|
+
// record; malformed frames are skipped.
|
|
64
59
|
export async function* readDaemonStream(body: ReadableStream<Uint8Array>): AsyncGenerator<Record<string, unknown>> {
|
|
65
60
|
for await (const frame of sseFrames(body)) {
|
|
66
61
|
const parsed = dataOf(frame);
|
package/src/surface.json
CHANGED
|
@@ -611,5 +611,89 @@
|
|
|
611
611
|
"repos",
|
|
612
612
|
"write"
|
|
613
613
|
]
|
|
614
|
+
},
|
|
615
|
+
"2.11.0": {
|
|
616
|
+
"manifest": [
|
|
617
|
+
"$schema",
|
|
618
|
+
"art",
|
|
619
|
+
"category",
|
|
620
|
+
"contributes",
|
|
621
|
+
"engines",
|
|
622
|
+
"entry",
|
|
623
|
+
"icon",
|
|
624
|
+
"logo",
|
|
625
|
+
"name",
|
|
626
|
+
"permissions",
|
|
627
|
+
"publisher",
|
|
628
|
+
"server",
|
|
629
|
+
"version"
|
|
630
|
+
],
|
|
631
|
+
"contributes": [
|
|
632
|
+
"agent",
|
|
633
|
+
"automationTemplates",
|
|
634
|
+
"bin",
|
|
635
|
+
"capabilities",
|
|
636
|
+
"commands",
|
|
637
|
+
"documents",
|
|
638
|
+
"environment",
|
|
639
|
+
"files",
|
|
640
|
+
"listener",
|
|
641
|
+
"processes",
|
|
642
|
+
"settings",
|
|
643
|
+
"viewers",
|
|
644
|
+
"views"
|
|
645
|
+
],
|
|
646
|
+
"api": [
|
|
647
|
+
"apiVersion",
|
|
648
|
+
"chat",
|
|
649
|
+
"commands",
|
|
650
|
+
"documents",
|
|
651
|
+
"href",
|
|
652
|
+
"models",
|
|
653
|
+
"navigate",
|
|
654
|
+
"processes",
|
|
655
|
+
"route",
|
|
656
|
+
"sandbox",
|
|
657
|
+
"settings",
|
|
658
|
+
"terminal",
|
|
659
|
+
"theme",
|
|
660
|
+
"viewers",
|
|
661
|
+
"views",
|
|
662
|
+
"workspace"
|
|
663
|
+
],
|
|
664
|
+
"listener": ["automation", "events", "provider"],
|
|
665
|
+
"sandboxApi": ["fetch", "json", "key", "origin", "reachable", "request", "role", "rpc"],
|
|
666
|
+
"moduleExports": [
|
|
667
|
+
"extensionApiVersion",
|
|
668
|
+
"flattenQuery",
|
|
669
|
+
"hostSlot",
|
|
670
|
+
"mergeQuery",
|
|
671
|
+
"readDaemonStream",
|
|
672
|
+
"resetSandboxScope",
|
|
673
|
+
"sandboxLedger",
|
|
674
|
+
"sandboxPoll",
|
|
675
|
+
"sandboxRef",
|
|
676
|
+
"sandboxScopeGuard",
|
|
677
|
+
"sandboxValue",
|
|
678
|
+
"satisfiesEngines"
|
|
679
|
+
],
|
|
680
|
+
"workspaceApi": [
|
|
681
|
+
"capabilities",
|
|
682
|
+
"file",
|
|
683
|
+
"fillDiff",
|
|
684
|
+
"onDidChange",
|
|
685
|
+
"onDidChangeFiles",
|
|
686
|
+
"onDidChangeRefs",
|
|
687
|
+
"openDiff",
|
|
688
|
+
"readJson",
|
|
689
|
+
"repos",
|
|
690
|
+
"write"
|
|
691
|
+
],
|
|
692
|
+
"chatApi": [
|
|
693
|
+
"composeLoop",
|
|
694
|
+
"composeWorkflow",
|
|
695
|
+
"openAgent",
|
|
696
|
+
"openSession"
|
|
697
|
+
]
|
|
614
698
|
}
|
|
615
699
|
}
|
package/src/version.ts
CHANGED
|
@@ -67,4 +67,9 @@
|
|
|
67
67
|
// the slowest tile sat ten minutes behind the file it described. Additive, and the recorded surface grew a
|
|
68
68
|
// `workspaceApi` member list with this release, the same grain `sandboxApi` got at 2.3.0 and for the same
|
|
69
69
|
// reason: this is where the addition happened, and nothing could see it.
|
|
70
|
-
|
|
70
|
+
// 2.11.0 adds `api.chat.openAgent`: open (or focus) the docked chat for a fleet agent by its id, the same
|
|
71
|
+
// thing a card press on the agents board does. The agent's conversation appears in the chat panel rather
|
|
72
|
+
// than navigating away from the current view. Two extensions were navigating to `/agents/<id>` on a plain
|
|
73
|
+
// click, which left the view they were on; this is the call they should have had. The `chatApi` sub-surface
|
|
74
|
+
// is recorded from this release on, for the same reason `workspaceApi` was recorded from 2.10.0.
|
|
75
|
+
export const extensionApiVersion = "2.11.0";
|