@otonoma/paracord-panels-interface 0.17.1-rc.12
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/channel.d.ts +14 -0
- package/dist/channel.d.ts.map +1 -0
- package/dist/channel.js +46 -0
- package/dist/client/abstractClient.d.ts +27 -0
- package/dist/client/abstractClient.d.ts.map +1 -0
- package/dist/client/abstractClient.js +22 -0
- package/dist/client/index.d.ts +6 -0
- package/dist/client/index.d.ts.map +1 -0
- package/dist/client/index.js +18 -0
- package/dist/client/managedPncpClient.d.ts +38 -0
- package/dist/client/managedPncpClient.d.ts.map +1 -0
- package/dist/client/managedPncpClient.js +156 -0
- package/dist/client/paracordClient.d.ts +28 -0
- package/dist/client/paracordClient.d.ts.map +1 -0
- package/dist/client/paracordClient.js +97 -0
- package/dist/client/pncpClient.d.ts +34 -0
- package/dist/client/pncpClient.d.ts.map +1 -0
- package/dist/client/pncpClient.js +234 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/interface/index.d.ts +43 -0
- package/dist/interface/index.d.ts.map +1 -0
- package/dist/interface/index.js +3 -0
- package/dist/interface/methods.d.ts +98 -0
- package/dist/interface/methods.d.ts.map +1 -0
- package/dist/interface/methods.js +1 -0
- package/package.json +35 -0
- package/src/channel.ts +58 -0
- package/src/client/abstractClient.ts +63 -0
- package/src/client/index.ts +20 -0
- package/src/client/managedPncpClient.ts +195 -0
- package/src/client/paracordClient.ts +149 -0
- package/src/client/pncpClient.ts +304 -0
- package/src/index.ts +2 -0
- package/src/interface/index.ts +74 -0
- package/src/interface/methods.ts +136 -0
- package/tsconfig.json +21 -0
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
// Conversation manager to make skill requests which expect a direct response able to wait directly on a
|
|
2
|
+
// response.
|
|
3
|
+
|
|
4
|
+
import { AbstractPncpClient, PncpCallResponse, RegisterObserverCallback, RegisterSkillCallback } from "../";
|
|
5
|
+
import { ObservationCallbackObject, PncpMessageCallbackObject, SkillRequest, PncpRequestCallbackObject, ConversationState, ConversationId, PncpQuestionMessage, PncpMessageKind, PncpAnswerMessage } from "@otonoma/paranet-client";
|
|
6
|
+
import AsyncQueue from "@ai-zen/async-queue";
|
|
7
|
+
|
|
8
|
+
export class ManagedPncpClient {
|
|
9
|
+
private convs: Map<number, (msg: PncpMessageCallbackObject) => void>;
|
|
10
|
+
private convStreams: Map<number, AsyncQueue<PncpMessageCallbackObject>>;
|
|
11
|
+
private observs: Map<number, (msg: ObservationCallbackObject) => void>;
|
|
12
|
+
private skills: Map<string, Map<number, (msg: PncpRequestCallbackObject) => void>>;
|
|
13
|
+
private id: number;
|
|
14
|
+
client: AbstractPncpClient;
|
|
15
|
+
|
|
16
|
+
constructor(base: AbstractPncpClient) {
|
|
17
|
+
this.convs = new Map();
|
|
18
|
+
this.convStreams = new Map();
|
|
19
|
+
this.observs = new Map();
|
|
20
|
+
this.skills = new Map();
|
|
21
|
+
this.id = new Date().getTime() + (Math.floor(Math.random() * 10000));
|
|
22
|
+
let old = base.msgCb;
|
|
23
|
+
base.msgCb = (msg: PncpMessageCallbackObject) => {
|
|
24
|
+
if (old)
|
|
25
|
+
old(msg);
|
|
26
|
+
if (msg.callback && 'pn/req/id' in msg.callback) {
|
|
27
|
+
let id = msg.callback['pn/req/id'] as number;
|
|
28
|
+
if (this.convs.has(id)) {
|
|
29
|
+
this.convs.get(id)!(msg);
|
|
30
|
+
this.convs.delete(id);
|
|
31
|
+
} else if (this.convStreams.has(id)) {
|
|
32
|
+
let queue = this.convStreams.get(id)!;
|
|
33
|
+
if (queue.isDone) {
|
|
34
|
+
this.convs.delete(id);
|
|
35
|
+
} else {
|
|
36
|
+
queue.push(msg);
|
|
37
|
+
if (msg.conversationState == ConversationState.STATE_COMPLETED || msg.conversationState == ConversationState.STATE_ERRORED) {
|
|
38
|
+
queue.done();
|
|
39
|
+
this.convs.delete(id);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
let oldObs = base.obsCb;
|
|
46
|
+
base.obsCb = (msg: ObservationCallbackObject, cb: any) => {
|
|
47
|
+
if (oldObs)
|
|
48
|
+
oldObs(msg);
|
|
49
|
+
console.log("Observer considering callback: ", cb, " on message ", msg);
|
|
50
|
+
if (cb && 'pn/obs/req/id' in cb) {
|
|
51
|
+
let id = cb['pn/obs/req/id'] as number;
|
|
52
|
+
this.observs.get(id)?.(msg);
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
let oldSk = base.skillCb;
|
|
57
|
+
base.skillCb = (msg: PncpRequestCallbackObject) => {
|
|
58
|
+
if (oldSk)
|
|
59
|
+
oldSk(msg);
|
|
60
|
+
|
|
61
|
+
let key = `${msg.body.subject}/${msg.body.action}`;
|
|
62
|
+
let inner = this.skills.get(key);
|
|
63
|
+
if (inner) {
|
|
64
|
+
for (let cb of inner.values()) {
|
|
65
|
+
cb(msg);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
this.client = base;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// A simple send request which expects _one_ response message.
|
|
73
|
+
// To make a call which can expect multiple responses, use `createConversation`.
|
|
74
|
+
public async sendRequest(req: SkillRequest): Promise<ConversationResponse> {
|
|
75
|
+
let id = this.id++;
|
|
76
|
+
if (!req.callback) {
|
|
77
|
+
req.callback = {};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
req.callback['pn/req/id'] = id;
|
|
81
|
+
let promise = new Promise<PncpMessageCallbackObject>((res) => {
|
|
82
|
+
this.convs.set(id, (pncp) => res(pncp));
|
|
83
|
+
});
|
|
84
|
+
let reply = await this.client.pncpRequest(req);
|
|
85
|
+
return new Promise(async (res) => {
|
|
86
|
+
let pncp = await promise;
|
|
87
|
+
res({ reply, pncp });
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async createConversation(req: SkillRequest): Promise<ConversationHandler> {
|
|
92
|
+
let id = this.id++;
|
|
93
|
+
if (!req.callback) {
|
|
94
|
+
req.callback = {};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
req.callback['pn/req/id'] = id;
|
|
98
|
+
|
|
99
|
+
let queue: AsyncQueue<PncpMessageCallbackObject> = new AsyncQueue();
|
|
100
|
+
|
|
101
|
+
this.convStreams.set(id, queue);
|
|
102
|
+
let reply = await this.client.pncpRequest(req);
|
|
103
|
+
return new ConversationHandler(this, queue, reply);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
public async createObserver(obs: RegisterObserverCallback, cb: (msg: ObservationCallbackObject) => void): Promise<ObserverHandle> {
|
|
107
|
+
let id = this.id++;
|
|
108
|
+
if (!obs.callback) {
|
|
109
|
+
obs.callback = {};
|
|
110
|
+
}
|
|
111
|
+
obs.callback['pn/obs/req/id'] = id;
|
|
112
|
+
this.observs.set(id, cb);
|
|
113
|
+
// NOTE: This can be clever enough to avoid duplicating these callbacks, but the paracord side can manage
|
|
114
|
+
// that too, so this should be OK.
|
|
115
|
+
// TODO: Does this return something useful?
|
|
116
|
+
let { args: { key } } = await this.client.registerObserverCallback(obs);
|
|
117
|
+
return new ObserverHandle(this, id, key);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async deleteObserver(id: number, key: string) {
|
|
121
|
+
this.observs.delete(id);
|
|
122
|
+
await this.client.deleteObserver(key);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public async createSkillCallback(sk: RegisterSkillCallback, cb: (msg: PncpRequestCallbackObject) => void): Promise<number> {
|
|
126
|
+
let key = `${sk.subject}/${sk.action}`;
|
|
127
|
+
if (!this.skills.has(key)) {
|
|
128
|
+
await this.client.registerSkillCallback(sk);
|
|
129
|
+
this.skills.set(key, new Map());
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
let inner = this.skills.get(key)!;
|
|
133
|
+
let id = this.id++;
|
|
134
|
+
inner.set(id, cb);
|
|
135
|
+
return id;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export interface ConversationResponse {
|
|
140
|
+
reply: PncpCallResponse;
|
|
141
|
+
pncp: PncpMessageCallbackObject;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
export class ConversationHandler {
|
|
145
|
+
private queue: AsyncGenerator<PncpMessageCallbackObject>;
|
|
146
|
+
private id: ConversationId;
|
|
147
|
+
private client: ManagedPncpClient;
|
|
148
|
+
|
|
149
|
+
constructor(client: ManagedPncpClient, queue: AsyncQueue<PncpMessageCallbackObject>, reply: PncpCallResponse) {
|
|
150
|
+
this.queue = queue[Symbol.asyncIterator]();
|
|
151
|
+
// TODO: Throw an error here if this is empty.
|
|
152
|
+
this.id = reply.conversationId!;
|
|
153
|
+
this.client = client;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
public async next(): Promise<PncpMessageCallbackObject | null> {
|
|
157
|
+
return (await this.queue.next()).value;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public async sendQuestion(req: PncpQuestionMessage): Promise<PncpCallResponse> {
|
|
161
|
+
return await this.client.client.pncpMessage({
|
|
162
|
+
id: this.id,
|
|
163
|
+
body: {
|
|
164
|
+
type: PncpMessageKind.PNCP_QUESTION,
|
|
165
|
+
value: req,
|
|
166
|
+
},
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
public async sendAnswer(req: PncpAnswerMessage): Promise<PncpCallResponse> {
|
|
171
|
+
return await this.client.client.pncpMessage({
|
|
172
|
+
id: this.id,
|
|
173
|
+
body: {
|
|
174
|
+
type: PncpMessageKind.PNCP_ANSWER,
|
|
175
|
+
value: req,
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
export class ObserverHandle {
|
|
182
|
+
private id: number;
|
|
183
|
+
private key: string;
|
|
184
|
+
private client: ManagedPncpClient;
|
|
185
|
+
|
|
186
|
+
constructor(client: ManagedPncpClient, id: number, key: string) {
|
|
187
|
+
this.client = client;
|
|
188
|
+
this.id = id;
|
|
189
|
+
this.key = key;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
public async close() {
|
|
193
|
+
await this.client.deleteObserver(this.id, this.key);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
// Client for panels to connect to paracord and send requests.
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
DocumentArgs,
|
|
5
|
+
LoginCredentials,
|
|
6
|
+
ObservationCallbackObject,
|
|
7
|
+
PncpMessageCallbackObject,
|
|
8
|
+
PncpMessageObject,
|
|
9
|
+
PncpRequestCallbackObject,
|
|
10
|
+
SkillRequest,
|
|
11
|
+
} from "@otonoma/paranet-client";
|
|
12
|
+
import { Channel } from "../channel";
|
|
13
|
+
import {
|
|
14
|
+
PanelConnect,
|
|
15
|
+
PanelMethodCall,
|
|
16
|
+
PARACORD_PANEL_REVISION,
|
|
17
|
+
} from "../interface";
|
|
18
|
+
import {
|
|
19
|
+
PncpMessageResponse,
|
|
20
|
+
MethodCall,
|
|
21
|
+
RegisterSkillCallback,
|
|
22
|
+
PncpCallResponse,
|
|
23
|
+
RegisterObserverCallback,
|
|
24
|
+
GraphqlCallArgs,
|
|
25
|
+
GraphqlCallResponse,
|
|
26
|
+
UploadDocumentArgs,
|
|
27
|
+
UploadDocumentResponse,
|
|
28
|
+
FetchDocumentResponse,
|
|
29
|
+
RegisterObserverResponse,
|
|
30
|
+
} from "../interface/methods";
|
|
31
|
+
import { AbstractPncpClient, PncpClientInit } from "../";
|
|
32
|
+
|
|
33
|
+
export class ParacordClient extends AbstractPncpClient {
|
|
34
|
+
channel: Channel;
|
|
35
|
+
skillCb?: (msg: PncpRequestCallbackObject) => void;
|
|
36
|
+
msgCb?: (msg: PncpMessageCallbackObject) => void;
|
|
37
|
+
obsCb?: (msg: ObservationCallbackObject, callback?: any) => void;
|
|
38
|
+
constructor(init?: PncpClientInit) {
|
|
39
|
+
super(init);
|
|
40
|
+
// Provided by Paracord.
|
|
41
|
+
const panelId = window.name;
|
|
42
|
+
// TODO (JAB): All of the message handling requests should be abstracted in a way that can be generalized
|
|
43
|
+
// to run in non-panel specific environments. This will make it easier to develop new panels as totally
|
|
44
|
+
// independent apps instead of requiring the panel embedding. This should both enable quicker/easier
|
|
45
|
+
// development but also greater portability of the panel code.
|
|
46
|
+
const message: PanelConnect = {
|
|
47
|
+
apiRevision: PARACORD_PANEL_REVISION,
|
|
48
|
+
panelId,
|
|
49
|
+
type: "panelConnect",
|
|
50
|
+
};
|
|
51
|
+
const msgChan = new MessageChannel();
|
|
52
|
+
// TODO: Implement proper callback here.
|
|
53
|
+
const channel = new Channel(panelId, msgChan.port1, (msg) => {
|
|
54
|
+
console.log("Got callback:", msg);
|
|
55
|
+
switch (msg.body.type) {
|
|
56
|
+
case "pncpSkillCb":
|
|
57
|
+
this.skillCb?.(msg.body.body);
|
|
58
|
+
break;
|
|
59
|
+
case "pncpMessageCb":
|
|
60
|
+
this.msgCb?.(msg.body.body);
|
|
61
|
+
break;
|
|
62
|
+
case "pncpObservationCb":
|
|
63
|
+
this.obsCb?.(msg.body.body, msg.body.callback);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
this.channel = channel;
|
|
68
|
+
// Call the background.
|
|
69
|
+
console.log("Posting channel");
|
|
70
|
+
top?.postMessage(message, "*", [msgChan.port2]);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async issueCall(call: MethodCall): Promise<any> {
|
|
74
|
+
const body: PanelMethodCall = {
|
|
75
|
+
type: "call",
|
|
76
|
+
body: call,
|
|
77
|
+
};
|
|
78
|
+
const response = await this.channel.asyncSend(body);
|
|
79
|
+
if (response.type == "callReturn") {
|
|
80
|
+
return response.body;
|
|
81
|
+
} else if (response.type == "paracordError") {
|
|
82
|
+
throw Error(response.error);
|
|
83
|
+
} else {
|
|
84
|
+
throw Error("Incorrect response type.");
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
public async login(_creds: LoginCredentials) { }
|
|
90
|
+
|
|
91
|
+
public async pncpRequest(req: SkillRequest): Promise<PncpCallResponse> {
|
|
92
|
+
return await this.issueCall({ method: "pncpRequest", args: req });
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
public async pncpResponse(
|
|
96
|
+
req: PncpMessageResponse
|
|
97
|
+
): Promise<PncpCallResponse> {
|
|
98
|
+
return await this.issueCall({ method: "pncpResponse", args: req });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
public async pncpMessage(
|
|
102
|
+
req: PncpMessageObject
|
|
103
|
+
): Promise<PncpCallResponse> {
|
|
104
|
+
return await this.issueCall({ method: "pncpMessage", args: req });
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Registers the callback with paracord.
|
|
108
|
+
public async registerSkillCallback(req: RegisterSkillCallback) {
|
|
109
|
+
return await this.issueCall({ method: "registerSkillCallback", args: req });
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public async registerObserverCallback(req: RegisterObserverCallback): Promise<RegisterObserverResponse> {
|
|
113
|
+
return await this.issueCall({
|
|
114
|
+
method: "registerObserverCallback",
|
|
115
|
+
args: req,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
public async deleteObserver(key: string) {
|
|
120
|
+
await this.issueCall({ method: "unregisterObserverCallback", args: { key } })
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public async uploadDocument(args: UploadDocumentArgs): Promise<UploadDocumentResponse> {
|
|
124
|
+
return await this.issueCall({
|
|
125
|
+
method: "uploadDocumentCall",
|
|
126
|
+
args,
|
|
127
|
+
})
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async fetchDocument(args: DocumentArgs): Promise<FetchDocumentResponse> {
|
|
131
|
+
return await this.issueCall({
|
|
132
|
+
method: "fetchDocumentCall",
|
|
133
|
+
args,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
public async graphqlCall(args: GraphqlCallArgs): Promise<GraphqlCallResponse> {
|
|
138
|
+
return await this.issueCall({
|
|
139
|
+
method: "graphqlCall",
|
|
140
|
+
args,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export interface ParacordClientInit {
|
|
146
|
+
skill?: (msg: PncpRequestCallbackObject) => void;
|
|
147
|
+
msg?: (msg: PncpMessageCallbackObject) => void;
|
|
148
|
+
obs?: (msg: ObservationCallbackObject, callback?: any) => void;
|
|
149
|
+
}
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
// TODO: A lot of this code is copied from the pncpRpcClient found in the main paracord source code.
|
|
2
|
+
// As such, we should refactor that to make use of this code instead. We should likely move this into a more abstract location and not be
|
|
3
|
+
// part of the "panels" library, but something else instead. All of this is totally generic, and it could
|
|
4
|
+
// just live in the paranet-client repo instead, but then we would just need to implement it in the panels
|
|
5
|
+
// library specifically (which already depends on the paranet-client repo, so that would be OK).
|
|
6
|
+
import { DocumentArgs, LoginCredentials, ObservationCallback, ObservationCallbackObject, ObservationInit, ObservationMessageType, ObservationStartPoint, ParanetObserverListener, ParanetServiceClient, PncpCallback, PncpCallbackObject, PncpClient, PncpListener, PncpMessageCallbackObject, PncpMessageKind, PncpMessageObject, PncpRequestCallbackObject, simplifyObservationCallback, simplifyPncpCallback, SkillRequest, Timestamp, TokenResponse } from "@otonoma/paranet-client";
|
|
7
|
+
import { AbstractPncpClient, PncpClientInit } from ".";
|
|
8
|
+
import { FetchDocumentResponse, GraphqlCallArgs, GraphqlCallResponse, PncpCallResponse, PncpMessageResponse, RegisterObserverCallback, RegisterObserverResponse, RegisterSkillCallback, UploadDocumentArgs, UploadDocumentResponse } from "../interface";
|
|
9
|
+
|
|
10
|
+
export type ConnectionState =
|
|
11
|
+
| "connected"
|
|
12
|
+
| "disconnected"
|
|
13
|
+
| "pending"
|
|
14
|
+
| "unauthenticated"
|
|
15
|
+
| "failed";
|
|
16
|
+
|
|
17
|
+
interface CreateObserver {
|
|
18
|
+
body: ObservationInit;
|
|
19
|
+
offset?: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class PncpRpcClient extends AbstractPncpClient {
|
|
23
|
+
// TODO: We need a service client too for graphql calls.
|
|
24
|
+
private client: PncpClient;
|
|
25
|
+
private svcClient: ParanetServiceClient;
|
|
26
|
+
private listener: PncpListener;
|
|
27
|
+
private observerState: Map<string, ObserverState>;
|
|
28
|
+
|
|
29
|
+
actorId: string;
|
|
30
|
+
actorVersion: string;
|
|
31
|
+
|
|
32
|
+
// Maps `name -> ${subject}/${action} -> ObserverState` style key to an observer set.
|
|
33
|
+
connectionState: ConnectionState;
|
|
34
|
+
|
|
35
|
+
constructor(init: PncpRpcClientInit) {
|
|
36
|
+
super(init.init);
|
|
37
|
+
this.connectionState = "disconnected";
|
|
38
|
+
this.client = new PncpClient({
|
|
39
|
+
endpoint: init.broker,
|
|
40
|
+
actorId: init.actorId,
|
|
41
|
+
// TODO: We need to simplify this process of determining the version of client actors.
|
|
42
|
+
actorVersion: init.actorVersion,
|
|
43
|
+
token: init.tokens?.access_token,
|
|
44
|
+
refreshToken: init.tokens?.refresh_token,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
this.actorId = init.actorId;
|
|
48
|
+
this.actorVersion = init.actorVersion;
|
|
49
|
+
|
|
50
|
+
this.svcClient = new ParanetServiceClient({
|
|
51
|
+
actorId: init.actorId,
|
|
52
|
+
endpoint: init.service,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
this.client.setTokenCb((token, refresh) => {
|
|
56
|
+
this.svcClient.setTokens(token, refresh);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (init.tokens) {
|
|
60
|
+
this.svcClient.setTokens(init.tokens.access_token, init.tokens.refresh_token);
|
|
61
|
+
}
|
|
62
|
+
// TODO: Manage connection state, same as PncpRpcClient.
|
|
63
|
+
const listener = this.client
|
|
64
|
+
.pncpListener()
|
|
65
|
+
.onstart(() => {
|
|
66
|
+
this.connectionState = "connected";
|
|
67
|
+
// TODO: Trigger a callback here.
|
|
68
|
+
// useConnectionStore
|
|
69
|
+
// .getState()
|
|
70
|
+
// .setConnectionState(this.connectionState);
|
|
71
|
+
// resolve();
|
|
72
|
+
})
|
|
73
|
+
.ondisconnect(() => {
|
|
74
|
+
this.connectionState = "disconnected";
|
|
75
|
+
})
|
|
76
|
+
.onreconnect(() => {
|
|
77
|
+
this.connectionState = "connected";
|
|
78
|
+
})
|
|
79
|
+
.onmessage((msg: PncpCallback) => {
|
|
80
|
+
const simplified = simplifyPncpCallback(msg);
|
|
81
|
+
switch (simplified.type) {
|
|
82
|
+
case "skill":
|
|
83
|
+
{
|
|
84
|
+
this.skillCb?.(simplified);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
case "message":
|
|
88
|
+
{
|
|
89
|
+
this.msgCb?.(simplified);
|
|
90
|
+
}
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
})
|
|
94
|
+
.onerror((err, isFatal) => {
|
|
95
|
+
// this.error = decodeURIComponent(err);
|
|
96
|
+
this.connectionState = isFatal ? "unauthenticated" : "failed";
|
|
97
|
+
// reject(this.error);
|
|
98
|
+
})
|
|
99
|
+
.autoReconnect(true);
|
|
100
|
+
|
|
101
|
+
this.listener = listener;
|
|
102
|
+
this.observerState = new Map();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public async login(creds: LoginCredentials) {
|
|
106
|
+
let tokens = await this.client.login(creds);
|
|
107
|
+
this.svcClient.setTokens(tokens.access_token, tokens.refresh_token);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public async pncpRequest(req: SkillRequest): Promise<PncpCallResponse> {
|
|
111
|
+
let res = await this.client.pncpRequest(req);
|
|
112
|
+
|
|
113
|
+
const date =
|
|
114
|
+
res.timeCreated && Timestamp.toDate(res.timeCreated);
|
|
115
|
+
return {
|
|
116
|
+
type: "response",
|
|
117
|
+
messageId: res.messageId,
|
|
118
|
+
conversationId: res.id,
|
|
119
|
+
actorId: res.matchedId?.id,
|
|
120
|
+
actorVersion: res.matchedId?.version,
|
|
121
|
+
date,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public async pncpResponse(
|
|
126
|
+
req: PncpMessageResponse
|
|
127
|
+
): Promise<PncpCallResponse> {
|
|
128
|
+
let response = await this.client.pncpMessage({
|
|
129
|
+
conversation: req.conversation,
|
|
130
|
+
message: {
|
|
131
|
+
type: PncpMessageKind.PNCP_RESPONSE,
|
|
132
|
+
value: {
|
|
133
|
+
data: req.data,
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
const date = response.timeCreated && Timestamp.toDate(response.timeCreated);
|
|
139
|
+
const messageId = response.messageId;
|
|
140
|
+
const entityId = response.matchedId;
|
|
141
|
+
const actorId = entityId?.id;
|
|
142
|
+
const actorVersion = entityId?.version;
|
|
143
|
+
|
|
144
|
+
return { type: "response", date, messageId, actorId, actorVersion };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
public async pncpMessage(data: PncpMessageObject): Promise<PncpCallResponse> {
|
|
148
|
+
|
|
149
|
+
const response = await this.client.pncpMessageObject(data);
|
|
150
|
+
|
|
151
|
+
const date = response.timeCreated && Timestamp.toDate(response.timeCreated);
|
|
152
|
+
const messageId = response.messageId;
|
|
153
|
+
const entityId = response.matchedId;
|
|
154
|
+
const actorId = entityId?.id;
|
|
155
|
+
const actorVersion = entityId?.version;
|
|
156
|
+
|
|
157
|
+
return { type: "response", date, messageId, actorId, actorVersion };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
public async registerSkillCallback(_req: RegisterSkillCallback) {
|
|
161
|
+
// Nothing to do. We could have the paranet only send skill requests to certain clients,
|
|
162
|
+
// like the way observers work now - probably a useful feature. But for now, nothing to do here.
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
public async registerObserverCallback(req: RegisterObserverCallback): Promise<RegisterObserverResponse> {
|
|
166
|
+
if (!this.obsCb) {
|
|
167
|
+
throw new Error("Set an observer callback first");
|
|
168
|
+
}
|
|
169
|
+
const state = this.createOrGetObserverStream({
|
|
170
|
+
body: {
|
|
171
|
+
subject: req.subject,
|
|
172
|
+
action: req.action,
|
|
173
|
+
conversationId: req.conversationId,
|
|
174
|
+
filters: req.filters !== undefined ? req.filters : [{ kind: ObservationMessageType.OBS_MESSAGE_TYPE_SKILL }],
|
|
175
|
+
},
|
|
176
|
+
offset: req.offset,
|
|
177
|
+
});
|
|
178
|
+
console.log(state);
|
|
179
|
+
state.observer
|
|
180
|
+
.onstart(() => {
|
|
181
|
+
console.log("Started observer stream", req);
|
|
182
|
+
})
|
|
183
|
+
.ondisconnect(() => {
|
|
184
|
+
console.log("Observer disconnected");
|
|
185
|
+
})
|
|
186
|
+
.onerror((e) => {
|
|
187
|
+
console.log("Error from observer:", e);
|
|
188
|
+
})
|
|
189
|
+
.onmessage((msg: ObservationCallback) => {
|
|
190
|
+
const simplified = simplifyObservationCallback(msg);
|
|
191
|
+
this.obsCb?.(simplified, req.callback);
|
|
192
|
+
})
|
|
193
|
+
.autoReconnect(true)
|
|
194
|
+
.connect();
|
|
195
|
+
return {
|
|
196
|
+
type: "registerObserverCallback",
|
|
197
|
+
args: {
|
|
198
|
+
key: state.key
|
|
199
|
+
}
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
private createOrGetObserverStream({
|
|
204
|
+
body,
|
|
205
|
+
offset,
|
|
206
|
+
}: CreateObserver): ObserverState {
|
|
207
|
+
if (!body.subject || !body.action) {
|
|
208
|
+
// TODO: Throw an error.
|
|
209
|
+
}
|
|
210
|
+
const key = JSON.stringify({ body, offset })
|
|
211
|
+
.split("")
|
|
212
|
+
.sort()
|
|
213
|
+
.join("");
|
|
214
|
+
|
|
215
|
+
let state = this.observerState.get(key);
|
|
216
|
+
if (state != null) {
|
|
217
|
+
return state;
|
|
218
|
+
}
|
|
219
|
+
// NOTE (JAB, 2025-11-03): We will need to consider if we should allow panels to specify the start point, or just compute
|
|
220
|
+
// it ourselves. For now, probably better to restrict it.
|
|
221
|
+
const startPoint = offset
|
|
222
|
+
? ObservationStartPoint.OBS_START_OFFSET
|
|
223
|
+
: body.conversationId
|
|
224
|
+
? ObservationStartPoint.OBS_START_BEGINNING
|
|
225
|
+
: ObservationStartPoint.OBS_START_HEAD;
|
|
226
|
+
const listener = this.client.observerListener({
|
|
227
|
+
author: {
|
|
228
|
+
id: this.actorId,
|
|
229
|
+
version: this.actorVersion,
|
|
230
|
+
},
|
|
231
|
+
body,
|
|
232
|
+
startPoint,
|
|
233
|
+
seqOffset: offset ? BigInt(offset) : undefined,
|
|
234
|
+
// TODO (JAB, 2025-11-04): There may be scenarios where we want to track paracord observers,
|
|
235
|
+
// but currently this is only used in panels and we never want those to be tracked.
|
|
236
|
+
untracked: true,
|
|
237
|
+
});
|
|
238
|
+
// TODO: Setup the callback information correctly.
|
|
239
|
+
const newState: ObserverState = {
|
|
240
|
+
key,
|
|
241
|
+
observer: listener,
|
|
242
|
+
};
|
|
243
|
+
|
|
244
|
+
this.observerState.set(key, newState);
|
|
245
|
+
return newState;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
public async deleteObserver(key: string): Promise<void> {
|
|
249
|
+
console.log("Removing observer ", key);
|
|
250
|
+
const state = this.observerState.get(key);
|
|
251
|
+
if (state) {
|
|
252
|
+
state.observer.disconnect();
|
|
253
|
+
this.observerState.delete(key);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
public async uploadDocument(args: UploadDocumentArgs): Promise<UploadDocumentResponse> {
|
|
258
|
+
let response = await this.svcClient.documentUpload(args.body, args.args)
|
|
259
|
+
return {
|
|
260
|
+
type: "uploadDocument",
|
|
261
|
+
body: response,
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
public async fetchDocument(args: DocumentArgs): Promise<FetchDocumentResponse> {
|
|
266
|
+
let stream;
|
|
267
|
+
let error;
|
|
268
|
+
try {
|
|
269
|
+
let response = await this.svcClient.documentFetch(args);
|
|
270
|
+
stream = response.body!;
|
|
271
|
+
} catch (e) {
|
|
272
|
+
error = e;
|
|
273
|
+
}
|
|
274
|
+
return {
|
|
275
|
+
type: "fetchDocument",
|
|
276
|
+
error,
|
|
277
|
+
stream,
|
|
278
|
+
};
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
public async graphqlCall(args: GraphqlCallArgs): Promise<GraphqlCallResponse> {
|
|
282
|
+
let response = await this.svcClient.graphql(args.query, args.variables);
|
|
283
|
+
return {
|
|
284
|
+
type: "graphqlCall",
|
|
285
|
+
data: response.data,
|
|
286
|
+
error: response.errors,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
interface ObserverState {
|
|
292
|
+
observer: ParanetObserverListener;
|
|
293
|
+
// Used to construct a key for a new observer listener.
|
|
294
|
+
key: string;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export interface PncpRpcClientInit {
|
|
298
|
+
init?: PncpClientInit,
|
|
299
|
+
actorId: string,
|
|
300
|
+
actorVersion: string,
|
|
301
|
+
broker: string,
|
|
302
|
+
service: string,
|
|
303
|
+
tokens?: TokenResponse,
|
|
304
|
+
}
|
package/src/index.ts
ADDED