@onereach/idw-apps 0.1.2-beta.6.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/README.md +411 -0
- package/dist/app/createIdwApp.d.ts +104 -0
- package/dist/app/createIdwApp.d.ts.map +1 -0
- package/dist/app/createIdwApp.js +151 -0
- package/dist/app/createIdwApp.js.map +1 -0
- package/dist/app/index.d.ts +15 -0
- package/dist/app/index.d.ts.map +1 -0
- package/dist/app/index.js +3 -0
- package/dist/app/index.js.map +1 -0
- package/dist/app/installIdwAppApi.d.ts +98 -0
- package/dist/app/installIdwAppApi.d.ts.map +1 -0
- package/dist/app/installIdwAppApi.js +314 -0
- package/dist/app/installIdwAppApi.js.map +1 -0
- package/dist/createIdwAppBridge.d.ts +13 -0
- package/dist/createIdwAppBridge.d.ts.map +1 -0
- package/dist/createIdwAppBridge.js +555 -0
- package/dist/createIdwAppBridge.js.map +1 -0
- package/dist/hostStateChannel.d.ts +51 -0
- package/dist/hostStateChannel.d.ts.map +1 -0
- package/dist/hostStateChannel.js +104 -0
- package/dist/hostStateChannel.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol.d.ts +81 -0
- package/dist/protocol.d.ts.map +1 -0
- package/dist/protocol.js +67 -0
- package/dist/protocol.js.map +1 -0
- package/dist/stateStore.d.ts +23 -0
- package/dist/stateStore.d.ts.map +1 -0
- package/dist/stateStore.js +70 -0
- package/dist/stateStore.js.map +1 -0
- package/dist/types.d.ts +268 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +44 -0
- package/skills/idw-app-development/SKILL.md +458 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { IdwAppMessageType, envelope, isIdwAppToHostMessage, } from './protocol';
|
|
2
|
+
import { cloneJsonSerializableState } from './stateStore';
|
|
3
|
+
/**
|
|
4
|
+
* Host half of the IDW state channel.
|
|
5
|
+
*
|
|
6
|
+
* IDW extension (not part of the standard MCP Apps bridge): listens for
|
|
7
|
+
* `idw:app:*` messages coming from the target iframe and answers get/set-state
|
|
8
|
+
* requests. Every inbound message is validated against `iframe.contentWindow`
|
|
9
|
+
* so a sibling frame cannot spoof state traffic.
|
|
10
|
+
*/
|
|
11
|
+
export function createHostStateChannel(options) {
|
|
12
|
+
const { hostWindow, iframe, getState, applyAppState, handleAction, expectedOrigin, resolveTargetOrigin, } = options;
|
|
13
|
+
// Standard external MCP Apps do not install this channel. Do not send
|
|
14
|
+
// unsolicited IDW frames to them; an inbound IDW message is the app's opt-in.
|
|
15
|
+
let appHasUsedIdwChannel = false;
|
|
16
|
+
function postToApp(message) {
|
|
17
|
+
const target = iframe.contentWindow;
|
|
18
|
+
if (!target) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
target.postMessage(envelope(message), resolveTargetOrigin(iframe));
|
|
22
|
+
}
|
|
23
|
+
function errorResult(message) {
|
|
24
|
+
return {
|
|
25
|
+
content: [{
|
|
26
|
+
type: 'text',
|
|
27
|
+
text: message,
|
|
28
|
+
}],
|
|
29
|
+
isError: true,
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
async function handleMessage(event) {
|
|
33
|
+
// Security: only trust messages coming from *this* app iframe.
|
|
34
|
+
if (event.source !== iframe.contentWindow) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
if (expectedOrigin
|
|
38
|
+
&& expectedOrigin !== '*'
|
|
39
|
+
&& event.origin !== undefined
|
|
40
|
+
&& event.origin !== expectedOrigin) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
const message = event.data;
|
|
44
|
+
if (!isIdwAppToHostMessage(message)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
appHasUsedIdwChannel = true;
|
|
48
|
+
event.stopImmediatePropagation?.();
|
|
49
|
+
if (message.type === IdwAppMessageType.GetState) {
|
|
50
|
+
postToApp({
|
|
51
|
+
type: IdwAppMessageType.StateResult,
|
|
52
|
+
requestId: message.requestId,
|
|
53
|
+
state: cloneJsonSerializableState(getState()),
|
|
54
|
+
});
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (message.type === IdwAppMessageType.CallAction) {
|
|
58
|
+
let result;
|
|
59
|
+
try {
|
|
60
|
+
result = handleAction
|
|
61
|
+
? await handleAction(message.action)
|
|
62
|
+
: errorResult('No IDW action handler is configured for this app');
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
result = errorResult(error instanceof Error ? error.message : String(error));
|
|
66
|
+
}
|
|
67
|
+
postToApp({
|
|
68
|
+
type: IdwAppMessageType.ActionResult,
|
|
69
|
+
requestId: message.requestId,
|
|
70
|
+
result,
|
|
71
|
+
});
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
// SetState: update local state first (never block the iframe on persistence),
|
|
75
|
+
// then acknowledge with the stored value.
|
|
76
|
+
applyAppState(cloneJsonSerializableState(message.state));
|
|
77
|
+
postToApp({
|
|
78
|
+
type: IdwAppMessageType.StateResult,
|
|
79
|
+
requestId: message.requestId ?? '',
|
|
80
|
+
state: cloneJsonSerializableState(getState()),
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
start() {
|
|
85
|
+
appHasUsedIdwChannel = false;
|
|
86
|
+
hostWindow.addEventListener('message', handleMessage, true);
|
|
87
|
+
},
|
|
88
|
+
stop() {
|
|
89
|
+
hostWindow.removeEventListener('message', handleMessage, true);
|
|
90
|
+
appHasUsedIdwChannel = false;
|
|
91
|
+
},
|
|
92
|
+
/** Push a host-initiated state update after the app has opted into the IDW channel. */
|
|
93
|
+
pushState(state) {
|
|
94
|
+
if (!appHasUsedIdwChannel) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
postToApp({
|
|
98
|
+
type: IdwAppMessageType.StateChanged,
|
|
99
|
+
state: cloneJsonSerializableState(state),
|
|
100
|
+
});
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=hostStateChannel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hostStateChannel.js","sourceRoot":"","sources":["../src/hostStateChannel.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { DEFAULT_IDW_AUTH_TOKEN_ACTION, createIdwAppBridge, } from './createIdwAppBridge';
|
|
2
|
+
export { AppBridge as McpAppBridge, PostMessageTransport as McpAppBridgePostMessageTransport, } from '@modelcontextprotocol/ext-apps/app-bridge';
|
|
3
|
+
export { createHostStateChannel } from './hostStateChannel';
|
|
4
|
+
export type { HostStateChannelOptions, HostWindowLike, IframeLike, MessageEventLike, WindowPostLike, } from './hostStateChannel';
|
|
5
|
+
export { StateStore, cloneJsonSerializableState } from './stateStore';
|
|
6
|
+
export { IDW_APP_PROTOCOL_VERSION, IdwAppMessageType, isIdwAppMessage, isIdwAppToHostMessage, isIdwHostToAppMessage, } from './protocol';
|
|
7
|
+
export type { IdwActionResultMessage, IdwAppMessage, IdwAppToHostMessage, IdwCallActionMessage, IdwHostToAppMessage, IdwGetStateMessage, IdwSetStateMessage, IdwStateChangedMessage, IdwStateResultMessage, } from './protocol';
|
|
8
|
+
export type { AppCapabilities, CallToolParams, CallToolResult, AppToolCallOptions, AppToolCallParams, AppToolCallResult, AppToolListOptions, AppToolListParams, AppToolListResult, CreateIdwAppBridgeOptions, DownloadFileParams, DownloadFileResult, HostCapabilities, HostContext, IdwActionParams, IdwActionResult, IdwAppAuthContext, IdwAppAuthOptions, IdwAppBridge, IdwAppSurface, IdwHostContextBlock, IdwHostInfo, IdwOrTokenProvider, IdwRuntimeContext, IdwRuntimeUpdate, ListPromptsParams, ListPromptsResult, ListResourceTemplatesParams, ListResourceTemplatesResult, ListResourcesParams, ListResourcesResult, MessageParams, MessageResult, OpenLinkParams, PromptListChangedParams, ReadResourceParams, ReadResourceResult, RequestDisplayModeParams, RequestDisplayModeResult, RequestTeardownParams, ResourceListChangedParams, SizeChangeParams, SupportedContentBlockModalities, ToolInputParams, ToolListChangedParams, ToolResultParams, UpdateModelContextParams, } from './types';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,SAAS,IAAI,YAAY,EACzB,oBAAoB,IAAI,gCAAgC,GACzD,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,YAAY,EACV,uBAAuB,EACvB,cAAc,EACd,UAAU,EACV,gBAAgB,EAChB,cAAc,GACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,sBAAsB,EACtB,aAAa,EACb,mBAAmB,EACnB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,kBAAkB,EAClB,sBAAsB,EACtB,qBAAqB,GACtB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,eAAe,EACf,cAAc,EACd,cAAc,EACd,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,kBAAkB,EAClB,iBAAiB,EACjB,iBAAiB,EACjB,yBAAyB,EACzB,kBAAkB,EAClB,kBAAkB,EAClB,gBAAgB,EAChB,WAAW,EACX,eAAe,EACf,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,YAAY,EACZ,aAAa,EACb,mBAAmB,EACnB,WAAW,EACX,kBAAkB,EAClB,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,iBAAiB,EACjB,2BAA2B,EAC3B,2BAA2B,EAC3B,mBAAmB,EACnB,mBAAmB,EACnB,aAAa,EACb,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,kBAAkB,EAClB,kBAAkB,EAClB,wBAAwB,EACxB,wBAAwB,EACxB,qBAAqB,EACrB,yBAAyB,EACzB,gBAAgB,EAChB,+BAA+B,EAC/B,eAAe,EACf,qBAAqB,EACrB,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,SAAS,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { DEFAULT_IDW_AUTH_TOKEN_ACTION, createIdwAppBridge, } from './createIdwAppBridge';
|
|
2
|
+
export { AppBridge as McpAppBridge, PostMessageTransport as McpAppBridgePostMessageTransport, } from '@modelcontextprotocol/ext-apps/app-bridge';
|
|
3
|
+
export { createHostStateChannel } from './hostStateChannel';
|
|
4
|
+
export { StateStore, cloneJsonSerializableState } from './stateStore';
|
|
5
|
+
export { IDW_APP_PROTOCOL_VERSION, IdwAppMessageType, isIdwAppMessage, isIdwAppToHostMessage, isIdwHostToAppMessage, } from './protocol';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,6BAA6B,EAC7B,kBAAkB,GACnB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,SAAS,IAAI,YAAY,EACzB,oBAAoB,IAAI,gCAAgC,GACzD,MAAM,2CAA2C,CAAC;AACnD,OAAO,EAAE,sBAAsB,EAAE,MAAM,oBAAoB,CAAC;AAQ5D,OAAO,EAAE,UAAU,EAAE,0BAA0B,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EACL,wBAAwB,EACxB,iBAAiB,EACjB,eAAe,EACf,qBAAqB,EACrB,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDW app <-> host message protocol.
|
|
3
|
+
*
|
|
4
|
+
* This is an IDW-specific extension that runs *alongside* the standard MCP Apps
|
|
5
|
+
* bridge (`@modelcontextprotocol/ext-apps`). MCP traffic keeps flowing over the
|
|
6
|
+
* bridge's own `PostMessageTransport`; this channel carries the lightweight
|
|
7
|
+
* IDW/OpenAI widget-state API and IDW host actions (`window.idw.app` / `window.openai`).
|
|
8
|
+
*
|
|
9
|
+
* Messages are namespaced so they are distinguishable from MCP JSON-RPC frames.
|
|
10
|
+
* Both sides validate `event.source`/target window before trusting a message and
|
|
11
|
+
* consume recognized IDW messages before the MCP postMessage transport parses them.
|
|
12
|
+
*/
|
|
13
|
+
export declare const IDW_APP_PROTOCOL_VERSION = 1;
|
|
14
|
+
export declare const IdwAppMessageType: {
|
|
15
|
+
/** App -> Host: request the current state snapshot. */
|
|
16
|
+
readonly GetState: "idw:app:get-state";
|
|
17
|
+
/** App -> Host: replace the whole state object. */
|
|
18
|
+
readonly SetState: "idw:app:set-state";
|
|
19
|
+
/** App -> Host: request an IDW host/platform action. */
|
|
20
|
+
readonly CallAction: "idw:app:call-action";
|
|
21
|
+
/** Host -> App: response to {@link IdwAppMessageType.GetState}/{@link IdwAppMessageType.SetState}. */
|
|
22
|
+
readonly StateResult: "idw:app:state-result";
|
|
23
|
+
/** Host -> App: host-initiated state update (e.g. initial state or external change). */
|
|
24
|
+
readonly StateChanged: "idw:app:state-changed";
|
|
25
|
+
/** Host -> App: response to {@link IdwAppMessageType.CallAction}. */
|
|
26
|
+
readonly ActionResult: "idw:app:action-result";
|
|
27
|
+
};
|
|
28
|
+
export type IdwAppMessageType = (typeof IdwAppMessageType)[keyof typeof IdwAppMessageType];
|
|
29
|
+
interface IdwAppMessageBase {
|
|
30
|
+
channel: 'idw:app';
|
|
31
|
+
version: number;
|
|
32
|
+
}
|
|
33
|
+
export interface IdwGetStateMessage extends IdwAppMessageBase {
|
|
34
|
+
type: typeof IdwAppMessageType.GetState;
|
|
35
|
+
requestId: string;
|
|
36
|
+
}
|
|
37
|
+
export interface IdwSetStateMessage extends IdwAppMessageBase {
|
|
38
|
+
type: typeof IdwAppMessageType.SetState;
|
|
39
|
+
requestId?: string;
|
|
40
|
+
state: unknown;
|
|
41
|
+
}
|
|
42
|
+
export interface IdwCallActionMessage extends IdwAppMessageBase {
|
|
43
|
+
type: typeof IdwAppMessageType.CallAction;
|
|
44
|
+
requestId: string;
|
|
45
|
+
action: {
|
|
46
|
+
name: string;
|
|
47
|
+
arguments?: Record<string, unknown>;
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export interface IdwStateResultMessage extends IdwAppMessageBase {
|
|
51
|
+
type: typeof IdwAppMessageType.StateResult;
|
|
52
|
+
requestId: string;
|
|
53
|
+
state: unknown;
|
|
54
|
+
}
|
|
55
|
+
export interface IdwStateChangedMessage extends IdwAppMessageBase {
|
|
56
|
+
type: typeof IdwAppMessageType.StateChanged;
|
|
57
|
+
state: unknown;
|
|
58
|
+
}
|
|
59
|
+
export interface IdwActionResultMessage extends IdwAppMessageBase {
|
|
60
|
+
type: typeof IdwAppMessageType.ActionResult;
|
|
61
|
+
requestId: string;
|
|
62
|
+
result: unknown;
|
|
63
|
+
}
|
|
64
|
+
export type IdwAppToHostMessage = IdwGetStateMessage | IdwSetStateMessage | IdwCallActionMessage;
|
|
65
|
+
export type IdwHostToAppMessage = IdwStateResultMessage | IdwStateChangedMessage | IdwActionResultMessage;
|
|
66
|
+
export type IdwAppMessage = IdwAppToHostMessage | IdwHostToAppMessage;
|
|
67
|
+
/** Type guard: is this an IDW app-channel message (any direction)? */
|
|
68
|
+
export declare function isIdwAppMessage(value: unknown): value is IdwAppMessage;
|
|
69
|
+
/** Type guard: message sent by the app to the host. */
|
|
70
|
+
export declare function isIdwAppToHostMessage(value: unknown): value is IdwAppToHostMessage;
|
|
71
|
+
/** Type guard: message sent by the host to the app. */
|
|
72
|
+
export declare function isIdwHostToAppMessage(value: unknown): value is IdwHostToAppMessage;
|
|
73
|
+
type DistributiveOmit<T, K extends keyof never> = T extends unknown ? Omit<T, K> : never;
|
|
74
|
+
/** An outgoing message without the shared channel/version envelope fields. */
|
|
75
|
+
export type IdwAppMessageBody = DistributiveOmit<IdwAppMessage, keyof IdwAppMessageBase>;
|
|
76
|
+
/** Attach the shared channel/version envelope to an outgoing message. */
|
|
77
|
+
export declare function envelope(message: IdwAppMessageBody): IdwAppMessage;
|
|
78
|
+
/** Generate a process-unique request id for correlating get-state responses. */
|
|
79
|
+
export declare function nextRequestId(): string;
|
|
80
|
+
export {};
|
|
81
|
+
//# sourceMappingURL=protocol.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,eAAO,MAAM,wBAAwB,IAAI,CAAC;AAE1C,eAAO,MAAM,iBAAiB;IAC5B,uDAAuD;;IAEvD,mDAAmD;;IAEnD,wDAAwD;;IAExD,sGAAsG;;IAEtG,wFAAwF;;IAExF,qEAAqE;;CAE7D,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAC3B,CAAC,OAAO,iBAAiB,CAAC,CAAC,MAAM,OAAO,iBAAiB,CAAC,CAAC;AAE7D,UAAU,iBAAiB;IACzB,OAAO,EAAE,SAAS,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,IAAI,EAAE,OAAO,iBAAiB,CAAC,QAAQ,CAAC;IACxC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,iBAAiB;IAC3D,IAAI,EAAE,OAAO,iBAAiB,CAAC,QAAQ,CAAC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,oBAAqB,SAAQ,iBAAiB;IAC7D,IAAI,EAAE,OAAO,iBAAiB,CAAC,UAAU,CAAC;IAC1C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE;QACN,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACrC,CAAC;CACH;AAED,MAAM,WAAW,qBAAsB,SAAQ,iBAAiB;IAC9D,IAAI,EAAE,OAAO,iBAAiB,CAAC,WAAW,CAAC;IAC3C,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,IAAI,EAAE,OAAO,iBAAiB,CAAC,YAAY,CAAC;IAC5C,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,sBAAuB,SAAQ,iBAAiB;IAC/D,IAAI,EAAE,OAAO,iBAAiB,CAAC,YAAY,CAAC;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,kBAAkB,GAAG,oBAAoB,CAAC;AACjG,MAAM,MAAM,mBAAmB,GAAG,qBAAqB,GAAG,sBAAsB,GAAG,sBAAsB,CAAC;AAC1G,MAAM,MAAM,aAAa,GAAG,mBAAmB,GAAG,mBAAmB,CAAC;AAQtE,sEAAsE;AACtE,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAKtE;AAED,uDAAuD;AACvD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAOlF;AAED,uDAAuD;AACvD,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,mBAAmB,CAOlF;AAED,KAAK,gBAAgB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,KAAK,IAAI,CAAC,SAAS,OAAO,GAAG,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC;AAEzF,8EAA8E;AAC9E,MAAM,MAAM,iBAAiB,GAAG,gBAAgB,CAAC,aAAa,EAAE,MAAM,iBAAiB,CAAC,CAAC;AAEzF,yEAAyE;AACzE,wBAAgB,QAAQ,CAAC,OAAO,EAAE,iBAAiB,GAAG,aAAa,CAMlE;AAID,gFAAgF;AAChF,wBAAgB,aAAa,IAAI,MAAM,CAGtC"}
|
package/dist/protocol.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* IDW app <-> host message protocol.
|
|
3
|
+
*
|
|
4
|
+
* This is an IDW-specific extension that runs *alongside* the standard MCP Apps
|
|
5
|
+
* bridge (`@modelcontextprotocol/ext-apps`). MCP traffic keeps flowing over the
|
|
6
|
+
* bridge's own `PostMessageTransport`; this channel carries the lightweight
|
|
7
|
+
* IDW/OpenAI widget-state API and IDW host actions (`window.idw.app` / `window.openai`).
|
|
8
|
+
*
|
|
9
|
+
* Messages are namespaced so they are distinguishable from MCP JSON-RPC frames.
|
|
10
|
+
* Both sides validate `event.source`/target window before trusting a message and
|
|
11
|
+
* consume recognized IDW messages before the MCP postMessage transport parses them.
|
|
12
|
+
*/
|
|
13
|
+
export const IDW_APP_PROTOCOL_VERSION = 1;
|
|
14
|
+
export const IdwAppMessageType = {
|
|
15
|
+
/** App -> Host: request the current state snapshot. */
|
|
16
|
+
GetState: 'idw:app:get-state',
|
|
17
|
+
/** App -> Host: replace the whole state object. */
|
|
18
|
+
SetState: 'idw:app:set-state',
|
|
19
|
+
/** App -> Host: request an IDW host/platform action. */
|
|
20
|
+
CallAction: 'idw:app:call-action',
|
|
21
|
+
/** Host -> App: response to {@link IdwAppMessageType.GetState}/{@link IdwAppMessageType.SetState}. */
|
|
22
|
+
StateResult: 'idw:app:state-result',
|
|
23
|
+
/** Host -> App: host-initiated state update (e.g. initial state or external change). */
|
|
24
|
+
StateChanged: 'idw:app:state-changed',
|
|
25
|
+
/** Host -> App: response to {@link IdwAppMessageType.CallAction}. */
|
|
26
|
+
ActionResult: 'idw:app:action-result',
|
|
27
|
+
};
|
|
28
|
+
const CHANNEL = 'idw:app';
|
|
29
|
+
function isRecord(value) {
|
|
30
|
+
return typeof value === 'object' && value !== null;
|
|
31
|
+
}
|
|
32
|
+
/** Type guard: is this an IDW app-channel message (any direction)? */
|
|
33
|
+
export function isIdwAppMessage(value) {
|
|
34
|
+
return isRecord(value)
|
|
35
|
+
&& value.channel === CHANNEL
|
|
36
|
+
&& value.version === IDW_APP_PROTOCOL_VERSION
|
|
37
|
+
&& typeof value.type === 'string';
|
|
38
|
+
}
|
|
39
|
+
/** Type guard: message sent by the app to the host. */
|
|
40
|
+
export function isIdwAppToHostMessage(value) {
|
|
41
|
+
return isIdwAppMessage(value)
|
|
42
|
+
&& (value.type === IdwAppMessageType.GetState
|
|
43
|
+
|| value.type === IdwAppMessageType.SetState
|
|
44
|
+
|| value.type === IdwAppMessageType.CallAction);
|
|
45
|
+
}
|
|
46
|
+
/** Type guard: message sent by the host to the app. */
|
|
47
|
+
export function isIdwHostToAppMessage(value) {
|
|
48
|
+
return isIdwAppMessage(value)
|
|
49
|
+
&& (value.type === IdwAppMessageType.StateResult
|
|
50
|
+
|| value.type === IdwAppMessageType.StateChanged
|
|
51
|
+
|| value.type === IdwAppMessageType.ActionResult);
|
|
52
|
+
}
|
|
53
|
+
/** Attach the shared channel/version envelope to an outgoing message. */
|
|
54
|
+
export function envelope(message) {
|
|
55
|
+
return {
|
|
56
|
+
channel: CHANNEL,
|
|
57
|
+
version: IDW_APP_PROTOCOL_VERSION,
|
|
58
|
+
...message,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
let requestCounter = 0;
|
|
62
|
+
/** Generate a process-unique request id for correlating get-state responses. */
|
|
63
|
+
export function nextRequestId() {
|
|
64
|
+
requestCounter += 1;
|
|
65
|
+
return `idw-app-${Date.now().toString(36)}-${requestCounter.toString(36)}`;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=protocol.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"protocol.js","sourceRoot":"","sources":["../src/protocol.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,CAAC;AAE1C,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAC/B,uDAAuD;IACvD,QAAQ,EAAE,mBAAmB;IAC7B,mDAAmD;IACnD,QAAQ,EAAE,mBAAmB;IAC7B,wDAAwD;IACxD,UAAU,EAAE,qBAAqB;IACjC,sGAAsG;IACtG,WAAW,EAAE,sBAAsB;IACnC,wFAAwF;IACxF,YAAY,EAAE,uBAAuB;IACrC,qEAAqE;IACrE,YAAY,EAAE,uBAAuB;CAC7B,CAAC;AAmDX,MAAM,OAAO,GAAG,SAAS,CAAC;AAE1B,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACrD,CAAC;AAED,sEAAsE;AACtE,MAAM,UAAU,eAAe,CAAC,KAAc;IAC5C,OAAO,QAAQ,CAAC,KAAK,CAAC;WACjB,KAAK,CAAC,OAAO,KAAK,OAAO;WACzB,KAAK,CAAC,OAAO,KAAK,wBAAwB;WAC1C,OAAO,KAAK,CAAC,IAAI,KAAK,QAAQ,CAAC;AACtC,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,OAAO,eAAe,CAAC,KAAK,CAAC;WACxB,CACD,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,QAAQ;eACtC,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,QAAQ;eACzC,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,UAAU,CAC/C,CAAC;AACN,CAAC;AAED,uDAAuD;AACvD,MAAM,UAAU,qBAAqB,CAAC,KAAc;IAClD,OAAO,eAAe,CAAC,KAAK,CAAC;WACxB,CACD,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,WAAW;eACzC,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,YAAY;eAC7C,KAAK,CAAC,IAAI,KAAK,iBAAiB,CAAC,YAAY,CACjD,CAAC;AACN,CAAC;AAOD,yEAAyE;AACzE,MAAM,UAAU,QAAQ,CAAC,OAA0B;IACjD,OAAO;QACL,OAAO,EAAE,OAAO;QAChB,OAAO,EAAE,wBAAwB;QACjC,GAAG,OAAO;KACM,CAAC;AACrB,CAAC;AAED,IAAI,cAAc,GAAG,CAAC,CAAC;AAEvB,gFAAgF;AAChF,MAAM,UAAU,aAAa;IAC3B,cAAc,IAAI,CAAC,CAAC;IACpB,OAAO,WAAW,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,IAAI,cAAc,CAAC,QAAQ,CAAC,EAAE,CAAC,EAAE,CAAC;AAC7E,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validate and clone widget state through JSON semantics before it crosses a
|
|
3
|
+
* postMessage / persistence boundary. `undefined` is allowed as the package's
|
|
4
|
+
* internal "no state yet" sentinel.
|
|
5
|
+
*/
|
|
6
|
+
export declare function cloneJsonSerializableState<T = unknown>(state: T): T;
|
|
7
|
+
/**
|
|
8
|
+
* Minimal in-memory holder for the durable widget/view state of a single app
|
|
9
|
+
* instance.
|
|
10
|
+
*
|
|
11
|
+
* State semantics (see README):
|
|
12
|
+
* - lightweight, JSON-serializable widget/view state only — never tool results;
|
|
13
|
+
* - `set` replaces the whole state object;
|
|
14
|
+
* - persistence is the caller's responsibility (via `onStateChange`), not this
|
|
15
|
+
* store's — it only tracks the latest value known to the wrapper.
|
|
16
|
+
*/
|
|
17
|
+
export declare class StateStore {
|
|
18
|
+
private state;
|
|
19
|
+
constructor(initialState?: unknown);
|
|
20
|
+
get(): unknown;
|
|
21
|
+
set(next: unknown): void;
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=stateStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateStore.d.ts","sourceRoot":"","sources":["../src/stateStore.ts"],"names":[],"mappings":"AA8CA;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CAMnE;AAED;;;;;;;;;GASG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAU;gBAEX,YAAY,CAAC,EAAE,OAAO;IAIlC,GAAG,IAAI,OAAO;IAId,GAAG,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI;CAGzB"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
function assertJsonSerializable(value, path = '$', seen = new WeakSet()) {
|
|
2
|
+
if (value === undefined) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
const valueType = typeof value;
|
|
6
|
+
if (valueType === 'string'
|
|
7
|
+
|| valueType === 'boolean'
|
|
8
|
+
|| value === null) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
if (valueType === 'number') {
|
|
12
|
+
if (!Number.isFinite(value)) {
|
|
13
|
+
throw new TypeError(`IDW app state must be JSON-serializable; ${path} is not a finite number`);
|
|
14
|
+
}
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
if (valueType === 'bigint' || valueType === 'function' || valueType === 'symbol') {
|
|
18
|
+
throw new TypeError(`IDW app state must be JSON-serializable; ${path} is ${valueType}`);
|
|
19
|
+
}
|
|
20
|
+
if (!value || valueType !== 'object') {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
if (seen.has(value)) {
|
|
24
|
+
throw new TypeError(`IDW app state must be JSON-serializable; ${path} contains a circular reference`);
|
|
25
|
+
}
|
|
26
|
+
seen.add(value);
|
|
27
|
+
if (Array.isArray(value)) {
|
|
28
|
+
value.forEach((item, index) => assertJsonSerializable(item, `${path}[${index}]`, seen));
|
|
29
|
+
seen.delete(value);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const [key, item] of Object.entries(value)) {
|
|
33
|
+
assertJsonSerializable(item, `${path}.${key}`, seen);
|
|
34
|
+
}
|
|
35
|
+
seen.delete(value);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Validate and clone widget state through JSON semantics before it crosses a
|
|
39
|
+
* postMessage / persistence boundary. `undefined` is allowed as the package's
|
|
40
|
+
* internal "no state yet" sentinel.
|
|
41
|
+
*/
|
|
42
|
+
export function cloneJsonSerializableState(state) {
|
|
43
|
+
assertJsonSerializable(state);
|
|
44
|
+
if (state === undefined) {
|
|
45
|
+
return state;
|
|
46
|
+
}
|
|
47
|
+
return JSON.parse(JSON.stringify(state));
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Minimal in-memory holder for the durable widget/view state of a single app
|
|
51
|
+
* instance.
|
|
52
|
+
*
|
|
53
|
+
* State semantics (see README):
|
|
54
|
+
* - lightweight, JSON-serializable widget/view state only — never tool results;
|
|
55
|
+
* - `set` replaces the whole state object;
|
|
56
|
+
* - persistence is the caller's responsibility (via `onStateChange`), not this
|
|
57
|
+
* store's — it only tracks the latest value known to the wrapper.
|
|
58
|
+
*/
|
|
59
|
+
export class StateStore {
|
|
60
|
+
constructor(initialState) {
|
|
61
|
+
this.state = cloneJsonSerializableState(initialState);
|
|
62
|
+
}
|
|
63
|
+
get() {
|
|
64
|
+
return this.state;
|
|
65
|
+
}
|
|
66
|
+
set(next) {
|
|
67
|
+
this.state = cloneJsonSerializableState(next);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=stateStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stateStore.js","sourceRoot":"","sources":["../src/stateStore.ts"],"names":[],"mappings":"AAAA,SAAS,sBAAsB,CAAC,KAAc,EAAE,IAAI,GAAG,GAAG,EAAE,OAAO,IAAI,OAAO,EAAU;IACtF,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO;IACT,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,KAAK,CAAC;IAC/B,IACE,SAAS,KAAK,QAAQ;WACnB,SAAS,KAAK,SAAS;WACvB,KAAK,KAAK,IAAI,EACjB,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC3B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,MAAM,IAAI,SAAS,CAAC,4CAA4C,IAAI,yBAAyB,CAAC,CAAC;QACjG,CAAC;QACD,OAAO;IACT,CAAC;IAED,IAAI,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,UAAU,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QACjF,MAAM,IAAI,SAAS,CAAC,4CAA4C,IAAI,OAAO,SAAS,EAAE,CAAC,CAAC;IAC1F,CAAC;IAED,IAAI,CAAC,KAAK,IAAI,SAAS,KAAK,QAAQ,EAAE,CAAC;QACrC,OAAO;IACT,CAAC;IAED,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACpB,MAAM,IAAI,SAAS,CAAC,4CAA4C,IAAI,gCAAgC,CAAC,CAAC;IACxG,CAAC;IACD,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEhB,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,sBAAsB,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,KAAK,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QACxF,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO;IACT,CAAC;IAED,KAAK,MAAM,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAChD,sBAAsB,CAAC,IAAI,EAAE,GAAG,IAAI,IAAI,GAAG,EAAE,EAAE,IAAI,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACrB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0BAA0B,CAAc,KAAQ;IAC9D,sBAAsB,CAAC,KAAK,CAAC,CAAC;IAC9B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAM,CAAC;AAChD,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,OAAO,UAAU;IAGrB,YAAY,YAAsB;QAChC,IAAI,CAAC,KAAK,GAAG,0BAA0B,CAAC,YAAY,CAAC,CAAC;IACxD,CAAC;IAED,GAAG;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED,GAAG,CAAC,IAAa;QACf,IAAI,CAAC,KAAK,GAAG,0BAA0B,CAAC,IAAI,CAAC,CAAC;IAChD,CAAC;CACF"}
|