@xopcai/extension-ui-sdk 0.0.313 → 0.0.315

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 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xopcai/extension-ui-sdk",
3
- "version": "0.0.313",
3
+ "version": "0.0.315",
4
4
  "description": "Gateway Console extension UI SDK (iframe postMessage client)",
5
5
  "repository": {
6
6
  "type": "git",
@@ -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>;