@ixo/oracle-runtime-workers 0.39.0 → 0.41.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/package.json +3 -2
- package/src/action-interactions/routes.ts +78 -0
- package/src/action-interactions/store.ts +1038 -0
- package/src/core/middlewares/tool-repetition-guard.ts +61 -1
- package/src/do/contracts.ts +32 -0
- package/src/do/user-oracle-do.ts +226 -3
- package/src/index.ts +2 -0
- package/src/plugins/asks/asks.plugin.ts +226 -0
- package/src/plugins/asks/attach.ts +342 -0
- package/src/plugins/asks/connection-do.ts +69 -0
- package/src/plugins/asks/connection.ts +368 -0
- package/src/plugins/asks/guide.ts +18 -0
- package/src/plugins/asks/index.ts +5 -0
- package/src/plugins/asks/oauth.ts +381 -0
- package/src/plugins/asks/routes.ts +258 -0
- package/src/plugins/asks/skill-pick.ts +114 -0
- package/src/plugins/asks/skills.bundled.json +27 -0
- package/src/plugins/asks/skills.ts +207 -0
- package/src/plugins/asks/testing.ts +147 -0
- package/src/plugins/asks/tools.ts +714 -0
- package/src/plugins/index.ts +8 -0
- package/src/plugins/portal/portal.plugin.ts +2 -1
- package/src/plugins/tasks/tasks-tools.ts +20 -20
- package/src/shell/app.ts +241 -6
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** Fakes shared by the asks tests. Not imported by the Worker. */
|
|
2
|
+
import type { RuntimeContext } from '../../plugin-api';
|
|
3
|
+
import { AsksConnection, type ConnectionStorage } from './connection';
|
|
4
|
+
|
|
5
|
+
export const MCP_URL = 'https://dev.asks.world/v1/mcp';
|
|
6
|
+
export const PUBLIC_URL = 'https://companion.devnet.ixo.earth';
|
|
7
|
+
export const TOKEN_KEY = 'test-key-0123456789abcdefghijklmnopqrstuvwxyz';
|
|
8
|
+
export const PERSON_ADDRESS = `ixo1${'q'.repeat(38)}`;
|
|
9
|
+
export const PERSON_DID = `did:ixo:${PERSON_ADDRESS}`;
|
|
10
|
+
export const OTHER_ADDRESS = `ixo1${'z'.repeat(38)}`;
|
|
11
|
+
|
|
12
|
+
export class MemoryStorage implements ConnectionStorage {
|
|
13
|
+
readonly map = new Map<string, unknown>();
|
|
14
|
+
|
|
15
|
+
get<T>(key: string): Promise<T | undefined> {
|
|
16
|
+
return Promise.resolve(this.map.get(key) as T | undefined);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
put(key: string, value: unknown): Promise<void> {
|
|
20
|
+
this.map.set(key, structuredClone(value));
|
|
21
|
+
return Promise.resolve();
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
delete(key: string): Promise<boolean> {
|
|
25
|
+
return Promise.resolve(this.map.delete(key));
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Call {
|
|
30
|
+
url: string;
|
|
31
|
+
init?: RequestInit;
|
|
32
|
+
form?: URLSearchParams;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A fetch that records each call and answers with `answer`. */
|
|
36
|
+
export function recordingFetch(
|
|
37
|
+
answer: (call: Call) => Response | Promise<Response>,
|
|
38
|
+
) {
|
|
39
|
+
const calls: Call[] = [];
|
|
40
|
+
const fetcher = (async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
41
|
+
const call: Call = { url: String(input), init };
|
|
42
|
+
if (typeof init?.body === 'string' && init.body.includes('='))
|
|
43
|
+
call.form = new URLSearchParams(init.body);
|
|
44
|
+
calls.push(call);
|
|
45
|
+
return answer(call);
|
|
46
|
+
}) as typeof fetch;
|
|
47
|
+
return { calls, fetcher };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export const json = (
|
|
51
|
+
body: unknown,
|
|
52
|
+
status = 200,
|
|
53
|
+
headers: Record<string, string> = {},
|
|
54
|
+
) =>
|
|
55
|
+
new Response(JSON.stringify(body), {
|
|
56
|
+
status,
|
|
57
|
+
headers: { 'content-type': 'application/json', ...headers },
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export const tokenAnswer = (n: number, expiresIn = 1800) =>
|
|
61
|
+
json({
|
|
62
|
+
access_token: `asks_at_${n}`,
|
|
63
|
+
refresh_token: `asks_rt_${n}`,
|
|
64
|
+
token_type: 'Bearer',
|
|
65
|
+
expires_in: expiresIn,
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
/** The connector's `initialize` answer for a token signed in as `address`. */
|
|
69
|
+
export const accountAnswer = (address = PERSON_ADDRESS) =>
|
|
70
|
+
json({
|
|
71
|
+
jsonrpc: '2.0',
|
|
72
|
+
id: 1,
|
|
73
|
+
result: {
|
|
74
|
+
protocolVersion: '2025-06-18',
|
|
75
|
+
instructions: `Signed in as ${address}: my_status, read_ask act as this person.`,
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A connection whose OAuth calls go to `fetcher`; its account checks (an MCP
|
|
81
|
+
* `initialize` on MCP_URL) go to `account`, recorded in `probes`.
|
|
82
|
+
*/
|
|
83
|
+
export function connectionWith(
|
|
84
|
+
fetcher: typeof fetch,
|
|
85
|
+
clock: { now: number },
|
|
86
|
+
storage = new MemoryStorage(),
|
|
87
|
+
account: (call: Call) => Response | Promise<Response> = () => accountAnswer(),
|
|
88
|
+
) {
|
|
89
|
+
const probes: Call[] = [];
|
|
90
|
+
const routed = ((input: RequestInfo | URL, init?: RequestInit) => {
|
|
91
|
+
if (String(input) === MCP_URL) {
|
|
92
|
+
const call: Call = { url: MCP_URL, init };
|
|
93
|
+
probes.push(call);
|
|
94
|
+
return Promise.resolve(account(call));
|
|
95
|
+
}
|
|
96
|
+
return fetcher(input, init);
|
|
97
|
+
}) as typeof fetch;
|
|
98
|
+
const connection = new AsksConnection({
|
|
99
|
+
storage,
|
|
100
|
+
config: { mcpUrl: MCP_URL, publicUrl: PUBLIC_URL, tokenKey: TOKEN_KEY },
|
|
101
|
+
fetch: routed,
|
|
102
|
+
now: () => clock.now,
|
|
103
|
+
});
|
|
104
|
+
return { connection, storage, probes };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export class MemoryKv {
|
|
108
|
+
readonly map = new Map<string, unknown>();
|
|
109
|
+
|
|
110
|
+
get(namespace: string, key: string): Promise<unknown> {
|
|
111
|
+
return Promise.resolve(this.map.get(`${namespace}/${key}`));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
set(namespace: string, key: string, value: unknown): Promise<void> {
|
|
115
|
+
this.map.set(`${namespace}/${key}`, structuredClone(value));
|
|
116
|
+
return Promise.resolve();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
update(): Promise<unknown> {
|
|
120
|
+
return Promise.reject(new Error('not used'));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
delete(namespace: string, key: string): Promise<void> {
|
|
124
|
+
this.map.delete(`${namespace}/${key}`);
|
|
125
|
+
return Promise.resolve();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export function fakeContext(
|
|
130
|
+
extra: Record<string, unknown> = {},
|
|
131
|
+
): RuntimeContext {
|
|
132
|
+
return {
|
|
133
|
+
user: { did: PERSON_DID },
|
|
134
|
+
session: { id: 's1', client: 'portal', requestId: 'r1' },
|
|
135
|
+
config: { NETWORK: 'devnet' },
|
|
136
|
+
loadedPlugins: new Set<string>(),
|
|
137
|
+
kv: new MemoryKv(),
|
|
138
|
+
logger: {
|
|
139
|
+
warn: () => undefined,
|
|
140
|
+
info: () => undefined,
|
|
141
|
+
error: () => undefined,
|
|
142
|
+
debug: () => undefined,
|
|
143
|
+
},
|
|
144
|
+
background: (work: Promise<unknown>) => void work,
|
|
145
|
+
...extra,
|
|
146
|
+
} as unknown as RuntimeContext;
|
|
147
|
+
}
|