@xopcai/extension-ui-sdk 0.0.313 → 0.0.314
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 +41 -0
- package/package.json +1 -1
- package/src/client.test.ts +10 -0
- package/src/client.ts +4 -0
- package/src/types.ts +6 -0
package/README.md
CHANGED
|
@@ -86,3 +86,44 @@ pnpm -C packages/extension-ui-sdk run docs
|
|
|
86
86
|
## License
|
|
87
87
|
|
|
88
88
|
MIT
|
|
89
|
+
# Pinned Local App capabilities
|
|
90
|
+
|
|
91
|
+
Installed Local Apps may declare `ui.capabilities` entries with an exact `id`,
|
|
92
|
+
`majorVersion`, and `descriptorDigest` from the authenticated Gateway catalog.
|
|
93
|
+
Currently supported bindings are `xopc.notes.get`, `xopc.notes.list`,
|
|
94
|
+
`xopc.tasks.get`, and `xopc.tasks.list` for reads, plus `xopc.notes.create`,
|
|
95
|
+
`xopc.notes.update`, and `xopc.notes.append` for local writes. Wildcards, duplicate
|
|
96
|
+
IDs, undeclared operations, and supplied implementation code are rejected.
|
|
97
|
+
|
|
98
|
+
Bindings are included in the release source hash and permission review. Obtain a
|
|
99
|
+
fresh acceptance result and user grant after changing the release. The UI host
|
|
100
|
+
binds calls to its registered iframe and reviewed release; never embed Gateway
|
|
101
|
+
credentials in the app or pass an extension identity, grant, or release digest
|
|
102
|
+
as capability arguments.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
const descriptor = await client.capability.describe('xopc.notes.list');
|
|
106
|
+
const result = await client.capability.call(descriptor.id, {
|
|
107
|
+
majorVersion: descriptor.majorVersion,
|
|
108
|
+
descriptorDigest: descriptor.descriptorDigest,
|
|
109
|
+
input: { limit: 20 },
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The host refuses undeclared or changed contracts, disabled/uninstalled apps, and
|
|
114
|
+
missing or revoked grants. A release switch or revocation during a read prevents
|
|
115
|
+
delivery of its result. This API currently targets installed Local Apps, not
|
|
116
|
+
arbitrary Node extensions. Writes require an explicit stable `idempotencyKey`;
|
|
117
|
+
retain it after timeouts or unknown outcomes. Keys are isolated by caller, app,
|
|
118
|
+
and installed release. Updates also require the current note revision.
|
|
119
|
+
|
|
120
|
+
Agents use `xopc_use` with `mode: "local_app"`, `command: "capabilities"`, and
|
|
121
|
+
`args: { extensionId }` to discover an already granted release. Invoke with
|
|
122
|
+
`command: "invoke"` and `args: { extensionId, manifestDigest, capabilityId, call }`.
|
|
123
|
+
Discovery never creates a grant. Both paths share the host dispatcher and release
|
|
124
|
+
checks. Task writes, destructive actions, arbitrary backend registrations, and
|
|
125
|
+
external writes are not exposed by these bindings.
|
|
126
|
+
|
|
127
|
+
Release validation and installation reject stale host contract digests; refresh
|
|
128
|
+
the binding and repeat acceptance before publishing. Rollback verifies the stored
|
|
129
|
+
artifact source hash and contract compatibility without reversing business data.
|
package/package.json
CHANGED
package/src/client.test.ts
CHANGED
|
@@ -4,6 +4,16 @@ import { createExtensionClient } from './client.js';
|
|
|
4
4
|
import type { Transport } from './transport.js';
|
|
5
5
|
|
|
6
6
|
describe('extension client product navigation', () => {
|
|
7
|
+
it('forwards the pinned capability contract without attaching caller authority', async () => {
|
|
8
|
+
const request = vi.fn(async () => ({ status: 'succeeded', releaseId: 'release', data: {} }));
|
|
9
|
+
const client = createExtensionClient({ transport: { request } as unknown as Transport });
|
|
10
|
+
const call = { majorVersion: 1, descriptorDigest: 'a'.repeat(64), input: {} };
|
|
11
|
+
await client.capability.describe('xopc.notes.list');
|
|
12
|
+
await client.capability.call('xopc.notes.list', call);
|
|
13
|
+
expect(request.mock.calls).toEqual([
|
|
14
|
+
['capability.describe', { id: 'xopc.notes.list' }], ['capability.call', { id: 'xopc.notes.list', call }],
|
|
15
|
+
]);
|
|
16
|
+
});
|
|
7
17
|
it('opens first-class product references through the host router', async () => {
|
|
8
18
|
const request = vi.fn(async () => undefined);
|
|
9
19
|
const transport = {
|
package/src/client.ts
CHANGED
|
@@ -14,6 +14,10 @@ export function createExtensionClient(options?: CreateExtensionClientOptions): E
|
|
|
14
14
|
const transport = options?.transport ?? new Transport();
|
|
15
15
|
|
|
16
16
|
const client: ExtensionClient = {
|
|
17
|
+
capability: {
|
|
18
|
+
describe: id => transport.request('capability.describe', { id }),
|
|
19
|
+
call: (id, call) => transport.request('capability.call', { id, call }),
|
|
20
|
+
},
|
|
17
21
|
async whenReady() {
|
|
18
22
|
await transport.ready;
|
|
19
23
|
},
|
package/src/types.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { CapabilityCall, CapabilityDescriptor } from '@xopcai/gateway-contract';
|
|
2
|
+
|
|
1
3
|
export enum ExtensionErrorCode {
|
|
2
4
|
PermissionDenied = 4001,
|
|
3
5
|
InvalidRequest = 4002,
|
|
@@ -62,6 +64,10 @@ export type ExtensionToHostMessage = ExtensionRequest | ExtensionEventMessage;
|
|
|
62
64
|
export type StreamHandler = (payload: unknown) => void;
|
|
63
65
|
|
|
64
66
|
export interface ExtensionClient {
|
|
67
|
+
capability: {
|
|
68
|
+
describe(id: string): Promise<CapabilityDescriptor>;
|
|
69
|
+
call(id: string, call: CapabilityCall): Promise<{ status: 'succeeded'; releaseId: string; data: unknown }>;
|
|
70
|
+
};
|
|
65
71
|
whenReady(): Promise<void>;
|
|
66
72
|
theme: {
|
|
67
73
|
getTheme(): Promise<ThemeInfo>;
|