@cef-ai/widget-runtime 1.1.1 → 2.0.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/bridge-wallet.js +19 -4
- package/dist/entry.js +36 -17
- package/dist/host-bridge.d.ts +59 -4
- package/dist/host-bridge.js +6 -1
- package/dist/manifest.d.ts +4 -1
- package/dist/manifest.js +12 -0
- package/dist/resolve-identity.d.ts +21 -1
- package/dist/resolve-identity.js +2 -0
- package/dist/runtime.d.ts +3 -0
- package/dist/runtime.js +20 -3
- package/dist/types.d.ts +60 -6
- package/dist/vault-client.d.ts +47 -2
- package/dist/vault-client.js +100 -56
- package/dist/vault-sdk-config.d.ts +26 -0
- package/dist/vault-sdk-config.js +37 -0
- package/dist/widget-runtime.js +145 -140
- package/package.json +2 -2
package/dist/vault-client.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// packages/widget-runtime/src/vault-client.ts
|
|
2
|
-
import {
|
|
2
|
+
import {} from './types.js';
|
|
3
3
|
/**
|
|
4
4
|
* Normalise the raw result from cubbies.query into a uniform QueryResult.
|
|
5
5
|
* The platform returns `{columns: string[], rows: unknown[][]}` (positional).
|
|
@@ -23,78 +23,123 @@ function normaliseQueryResult(raw) {
|
|
|
23
23
|
console.warn('[widget-runtime] unrecognized cubby query result shape; treating as empty', raw);
|
|
24
24
|
return { columns: [], rows: [], meta: { rowCount: 0 } };
|
|
25
25
|
}
|
|
26
|
+
export function createVaultClient(manifest, makeSdk,
|
|
26
27
|
/**
|
|
27
|
-
*
|
|
28
|
-
*
|
|
28
|
+
* The host's delegation, when it gave one — handed to `ensure()` as well as
|
|
29
|
+
* used for auth.
|
|
30
|
+
*
|
|
31
|
+
* WHY BOTH. With a token the SDK is built with `auth` and NO `signer`, on
|
|
32
|
+
* purpose: the handle structurally cannot sign, which is what keeps a keyless
|
|
33
|
+
* passkey session from stepping up on every request. But `vault.ensure()`
|
|
34
|
+
* does not look at `auth` — it needs a `delegationToken` or a `signer`, and
|
|
35
|
+
* with neither it throws before the first query runs:
|
|
36
|
+
*
|
|
37
|
+
* vault.ensure() needs either a `delegationToken` or a `signer` configured
|
|
38
|
+
* on VaultSDKConfig (the signer drives the on-chain vault-delegate grant).
|
|
39
|
+
*
|
|
40
|
+
* So a framed widget with a host delegation — the configuration this whole
|
|
41
|
+
* tier is built around — could authenticate every request and never reach
|
|
42
|
+
* one. The token IS the authorization, which is exactly the case `ensure`
|
|
43
|
+
* documents `delegationToken` for.
|
|
29
44
|
*/
|
|
30
|
-
|
|
31
|
-
if (err && typeof err === 'object') {
|
|
32
|
-
const status = err.status;
|
|
33
|
-
const message = err.message;
|
|
34
|
-
if (status === 404 ||
|
|
35
|
-
(typeof message === 'string' && /not.?connected|404/i.test(message))) {
|
|
36
|
-
throw new AgentNotConnectedError(agentId);
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
throw err;
|
|
40
|
-
}
|
|
41
|
-
export function createVaultClient(manifest, makeSdk) {
|
|
45
|
+
hostToken) {
|
|
42
46
|
let sdk;
|
|
43
47
|
let vaultP;
|
|
44
|
-
let agentHandleP;
|
|
45
48
|
const getSdk = () => (sdk ?? (sdk = makeSdk()));
|
|
46
49
|
const getVault = () => {
|
|
47
50
|
// `onboard: false` preserves the old lightweight ensure(): claim the vault
|
|
48
51
|
// with a wallet-built delegation token, skipping the gateway onboarding
|
|
49
52
|
// status/bootstrap round-trip (widgets assume a provisioned wallet).
|
|
50
|
-
vaultP ?? (vaultP = getSdk().vault.ensure({
|
|
53
|
+
vaultP ?? (vaultP = getSdk().vault.ensure({
|
|
54
|
+
onboard: false,
|
|
55
|
+
...(hostToken ? { delegationToken: hostToken } : {}),
|
|
56
|
+
}).catch((err) => {
|
|
51
57
|
vaultP = undefined;
|
|
52
58
|
throw err;
|
|
53
59
|
}));
|
|
54
60
|
return vaultP;
|
|
55
61
|
};
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
62
|
+
const declFor = (ref) => manifest.queries.find((q) => q.id === ref);
|
|
63
|
+
/**
|
|
64
|
+
* The agent service a widget belongs to — the half of its agent id before the
|
|
65
|
+
* colon.
|
|
66
|
+
*
|
|
67
|
+
* DERIVED, never declared. A widget that could name its own service could
|
|
68
|
+
* name somebody else's, and then a query's reach would be a field in a file
|
|
69
|
+
* uploaded to a content gateway rather than a property of who published it.
|
|
70
|
+
* This way the only service a widget can read is the one that shipped it.
|
|
71
|
+
*/
|
|
72
|
+
const ownService = () => {
|
|
73
|
+
const at = manifest.agentId.indexOf(':');
|
|
74
|
+
return at > 0 ? manifest.agentId.slice(0, at) : '';
|
|
75
|
+
};
|
|
76
|
+
/** Read the service's cubby — what this agent's siblings wrote. The default. */
|
|
77
|
+
const queryService = async (decl, params) => {
|
|
78
|
+
const vault = await getVault();
|
|
79
|
+
if (!vault.service) {
|
|
80
|
+
throw new Error('[widget-runtime] this host\'s vault-sdk has no agent-service cubby surface, '
|
|
81
|
+
+ `so the query "${decl.id}" cannot be answered here`);
|
|
82
|
+
}
|
|
83
|
+
const asPubKey = ownService();
|
|
84
|
+
if (!asPubKey) {
|
|
85
|
+
throw new Error(`[widget-runtime] "${decl.id}" reads the agent service's cubby, but `
|
|
86
|
+
+ `"${manifest.agentId}" carries no service key to read it from`);
|
|
87
|
+
}
|
|
88
|
+
// WHICH of the service's cubbies. `cubbyAlias` is the default, and it is
|
|
89
|
+
// legitimately EMPTY for a widget that declares no cubby of its own —
|
|
90
|
+
// `buildWidgetManifestForDecl` writes "" in that case — so a `service`
|
|
91
|
+
// query that also names none has no alias at all. Left unguarded that
|
|
92
|
+
// reaches vault-api as `/cubbies//query` and comes back as an alias-shape
|
|
93
|
+
// 400, which sends the author to look at their SQL.
|
|
94
|
+
const alias = decl.cubby ?? manifest.cubbyAlias;
|
|
95
|
+
if (!alias) {
|
|
96
|
+
throw new Error(`[widget-runtime] "${decl.id}" reads the agent service's cubby but names `
|
|
97
|
+
+ 'none, and this widget has no cubbyAlias to fall back on — set `cubby` on the query');
|
|
98
|
+
}
|
|
99
|
+
return vault.service(asPubKey).cubby(alias).query(decl.sql ?? '', params);
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Read the vault's memory bank.
|
|
103
|
+
*
|
|
104
|
+
* Named tools rather than SQL, because the bank is a gated graph API and not
|
|
105
|
+
* a table: the gate decides which records the caller may see, and a SQL
|
|
106
|
+
* string would imply a reach the surface does not offer.
|
|
107
|
+
*/
|
|
108
|
+
const queryMemory = async (decl, params) => {
|
|
109
|
+
const vault = await getVault();
|
|
110
|
+
if (!vault.memory) {
|
|
111
|
+
throw new Error('[widget-runtime] this host\'s vault-sdk has no memory surface, '
|
|
112
|
+
+ `so the query "${decl.id}" cannot be answered here`);
|
|
113
|
+
}
|
|
114
|
+
const first = params[0];
|
|
115
|
+
const limit = decl.limit;
|
|
116
|
+
switch (decl.tool ?? 'search') {
|
|
117
|
+
case 'search':
|
|
118
|
+
return vault.memory.search(String(first ?? ''), limit ? { limit } : undefined);
|
|
119
|
+
case 'get':
|
|
120
|
+
return vault.memory.get(String(first ?? ''));
|
|
121
|
+
case 'neighbours':
|
|
122
|
+
return vault.memory.neighbours(String(first ?? ''), limit ? { limit } : undefined);
|
|
123
|
+
case 'countByType':
|
|
124
|
+
return vault.memory.countByType();
|
|
125
|
+
default:
|
|
126
|
+
throw new Error(`[widget-runtime] "${decl.id}" names no memory tool this runtime knows`);
|
|
127
|
+
}
|
|
68
128
|
};
|
|
69
|
-
const resolveSql = (ref) => manifest.queries.find((q) => q.id === ref)?.sql ?? ref;
|
|
70
129
|
return {
|
|
71
130
|
async query(ref, params) {
|
|
72
|
-
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
let cubbyHandle;
|
|
83
|
-
try {
|
|
84
|
-
cubbyHandle = handle.cubby(manifest.cubbyAlias);
|
|
85
|
-
}
|
|
86
|
-
catch {
|
|
87
|
-
throw new AgentNotConnectedError(manifest.agentId);
|
|
88
|
-
}
|
|
89
|
-
raw = await cubbyHandle.query(resolveSql(ref), params ?? []);
|
|
90
|
-
}
|
|
91
|
-
catch (err) {
|
|
92
|
-
agentHandleP = undefined; // force a fresh handle on the next read
|
|
93
|
-
if (err instanceof AgentNotConnectedError)
|
|
94
|
-
throw err;
|
|
95
|
-
mapQueryError(err, manifest.agentId);
|
|
131
|
+
const decl = declFor(ref);
|
|
132
|
+
// TWO PLACES, AND THE DECLARATION SAYS WHICH. `tool` is the memory bank;
|
|
133
|
+
// anything else is SQL against one of the agent service's cubbies. A ref
|
|
134
|
+
// matching no declaration is raw SQL, which is a cubby read.
|
|
135
|
+
//
|
|
136
|
+
// Neither is agent-connection shaped — a cubby read addresses the
|
|
137
|
+
// SERVICE, the bank addresses the vault — so neither can fail as "agent
|
|
138
|
+
// not connected".
|
|
139
|
+
if (decl?.tool) {
|
|
140
|
+
return normaliseQueryResult(await queryMemory(decl, params ?? []));
|
|
96
141
|
}
|
|
97
|
-
return normaliseQueryResult(
|
|
142
|
+
return normaliseQueryResult(await queryService(decl ?? { id: ref, label: ref, sql: ref }, params ?? []));
|
|
98
143
|
},
|
|
99
144
|
async publish(type, payload, context) {
|
|
100
145
|
const vault = await getVault();
|
|
@@ -106,7 +151,6 @@ export function createVaultClient(manifest, makeSdk) {
|
|
|
106
151
|
// The vault-api resolves the manifest from the DDC registry and the SDK
|
|
107
152
|
// signs the GAR agreement internally — no marketplace.getAgent() fetch.
|
|
108
153
|
await vault.agents.connect({ agentId: manifest.agentId, scope: manifest.scope });
|
|
109
|
-
agentHandleP = undefined; // drop any cached pre-connect handle
|
|
110
154
|
},
|
|
111
155
|
async agentStatus() {
|
|
112
156
|
try {
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { VaultSDKConfig, Signer } from '@cef-ai/vault-sdk';
|
|
2
|
+
import type { WidgetManifest } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build the VaultSDK config the runtime constructs everywhere it talks to a
|
|
5
|
+
* user's vault — `buildVaultClient`'s SDK (queries/publish/connect) and the
|
|
6
|
+
* ADR-002 audio uploader's SDK. One builder so the two cannot drift.
|
|
7
|
+
*
|
|
8
|
+
* A TOKEN BEATS A SIGNER, when the host supplied one. `auth` overrides
|
|
9
|
+
* `signer` in `VaultSDKConfig`, so a config built with a delegation provider
|
|
10
|
+
* structurally CANNOT sign — which is the point: on the passkey wallet every
|
|
11
|
+
* signature from a keyless session raises a step-up, so a widget that signs per
|
|
12
|
+
* request raises a ceremony per request. The delegation was minted once, by a
|
|
13
|
+
* host that already had a session, and authenticates the same calls.
|
|
14
|
+
*
|
|
15
|
+
* `chainUrl` (the manifest's `endpoints.rpc`) is required by
|
|
16
|
+
* `vault.ensure()`'s wallet path: the SDK grants vault-api durable write
|
|
17
|
+
* delegation by submitting an on-chain `NonTransfer` proxy before claiming
|
|
18
|
+
* the vault, and needs a Cere node WS URL to do it. Both runtime call sites
|
|
19
|
+
* use `ensure({ onboard: false })` — that skips the gateway onboarding
|
|
20
|
+
* round-trip but NOT the grant step, so without `chainUrl` every widget boot
|
|
21
|
+
* would die on an actionable-but-user-visible config error. The manifest's
|
|
22
|
+
* `rpc` is baked per environment by the CLI's env table (devnet / testnet /
|
|
23
|
+
* mainnet), so the runtime inherits the deployment's chain instead of
|
|
24
|
+
* hard-coding one.
|
|
25
|
+
*/
|
|
26
|
+
export declare function toVaultSdkConfig(endpoints: WidgetManifest['endpoints'], signer: Signer, token?: string): VaultSDKConfig;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// packages/widget-runtime/src/vault-sdk-config.ts
|
|
2
|
+
import { DelegationAuthProvider } from '@cef-ai/vault-sdk';
|
|
3
|
+
/**
|
|
4
|
+
* Build the VaultSDK config the runtime constructs everywhere it talks to a
|
|
5
|
+
* user's vault — `buildVaultClient`'s SDK (queries/publish/connect) and the
|
|
6
|
+
* ADR-002 audio uploader's SDK. One builder so the two cannot drift.
|
|
7
|
+
*
|
|
8
|
+
* A TOKEN BEATS A SIGNER, when the host supplied one. `auth` overrides
|
|
9
|
+
* `signer` in `VaultSDKConfig`, so a config built with a delegation provider
|
|
10
|
+
* structurally CANNOT sign — which is the point: on the passkey wallet every
|
|
11
|
+
* signature from a keyless session raises a step-up, so a widget that signs per
|
|
12
|
+
* request raises a ceremony per request. The delegation was minted once, by a
|
|
13
|
+
* host that already had a session, and authenticates the same calls.
|
|
14
|
+
*
|
|
15
|
+
* `chainUrl` (the manifest's `endpoints.rpc`) is required by
|
|
16
|
+
* `vault.ensure()`'s wallet path: the SDK grants vault-api durable write
|
|
17
|
+
* delegation by submitting an on-chain `NonTransfer` proxy before claiming
|
|
18
|
+
* the vault, and needs a Cere node WS URL to do it. Both runtime call sites
|
|
19
|
+
* use `ensure({ onboard: false })` — that skips the gateway onboarding
|
|
20
|
+
* round-trip but NOT the grant step, so without `chainUrl` every widget boot
|
|
21
|
+
* would die on an actionable-but-user-visible config error. The manifest's
|
|
22
|
+
* `rpc` is baked per environment by the CLI's env table (devnet / testnet /
|
|
23
|
+
* mainnet), so the runtime inherits the deployment's chain instead of
|
|
24
|
+
* hard-coding one.
|
|
25
|
+
*/
|
|
26
|
+
export function toVaultSdkConfig(endpoints, signer, token) {
|
|
27
|
+
const base = {
|
|
28
|
+
endpoint: endpoints.vault,
|
|
29
|
+
garEndpoint: endpoints.gar,
|
|
30
|
+
marketplaceEndpoint: endpoints.marketplace,
|
|
31
|
+
s3GatewayAuthInfoUrl: endpoints.s3GatewayAuthInfo,
|
|
32
|
+
chainUrl: endpoints.rpc,
|
|
33
|
+
};
|
|
34
|
+
if (token)
|
|
35
|
+
return { ...base, auth: new DelegationAuthProvider(token) };
|
|
36
|
+
return { ...base, signer };
|
|
37
|
+
}
|