@metta-ts/das-client 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MesTTo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,72 @@
1
+ # @metta-ts/das-client
2
+
3
+ A client for SingularityNET's Distributed AtomSpace (DAS). It lets a MeTTa TS program query a remote, shared atomspace over gRPC, and it presents that DAS as a `Space` backend, so a DAS drops in wherever an in-memory space would. It is Node-only, because a participant hosts an inbound bus node; from the browser you reach a DAS through [`@metta-ts/das-gateway`](../das-gateway).
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install @metta-ts/das-client
9
+ ```
10
+
11
+ ## Querying a DAS
12
+
13
+ A DAS query is a network round-trip, so it is asynchronous. `DasLiveSpace` is the async analogue of an in-memory space, and `matchAsync` is the async analogue of `(match space pattern template)`: it queries the space and instantiates a template under each binding.
14
+
15
+ ```ts
16
+ import { DasLiveSpace, matchAsync } from "@metta-ts/das-client";
17
+ import { sym, expr, variable } from "@metta-ts/core";
18
+
19
+ const A = (...xs) => expr(xs);
20
+ const space = new DasLiveSpace(/* connection */);
21
+
22
+ const results = await matchAsync(space, A(sym("parent"), sym("Tom"), variable("c")));
23
+ console.log(results.map(String));
24
+ ```
25
+
26
+ For tests and local development, `MockTransport` exercises the same query path without a running DAS. Build a `DasSpace` over it, run your queries against canned answers, then swap in the real transport.
27
+
28
+ ## Running against a live DAS
29
+
30
+ The client is Node-only but cross-platform (Linux, macOS, Windows). The cluster below is stood up with `das-cli` + Docker; the steps and the one gotcha are what was verified on Linux. The loader (`db_loader`) and the Query Agent come from the same das image, so their atom handles agree.
31
+
32
+ ```bash
33
+ git clone https://github.com/singnet/das-toolbox.git
34
+ pip install -e das-toolbox/das-cli # in a fresh venv; the system das-cli may be stale
35
+ das-cli config set # accept defaults: Redis :40020, MongoDB :40021, AB :40001, QA :40002
36
+ das-cli db start # Redis + MongoDB
37
+ das-cli metta load /tmp/animals.metta # via the db_loader container; animals.metta is in hyperon-experimental/integration_tests/das
38
+ das-cli ab start # Attention Broker
39
+ das-cli qa start # Query Agent (serves pattern_matching_query)
40
+
41
+ DAS_LIVE=1 pnpm vitest run packages/das-client/src/live-query.test.ts
42
+ ```
43
+
44
+ Two things to know:
45
+
46
+ - **Query leaves are bare Symbols, not quoted strings.** `animals.metta` stores `is_animal`, `human`, etc. as Symbols, so build the pattern with `sym("is_animal")`, not `gstr("is_animal")` / `sym('"is_animal"')`. (An older das quoted string literals; that is gone.)
47
+ - **Linux only: pin MongoDB to 7.0.** On Linux kernels >= 6.19, das-cli's default `mongodb-community-server:8.x` refuses to start (`ERROR: ... tcmalloc ... known issue with the v6.19 and newer Linux kernel`). Set `MONGODB_IMAGE_NAME = "mongo"` and `MONGODB_IMAGE_VERSION = "7.0"` in das-cli's `settings/config.py` so `db start` succeeds. Other platforms (and older kernels) run the 8.x default fine.
48
+
49
+ A pattern with a variable then returns the matched bindings:
50
+
51
+ ```
52
+ (EVALUATION (PREDICATE is_animal) (CONCEPT $C))
53
+ -> chimp earthworm ent human monkey rhino snake triceratops
54
+ ```
55
+
56
+ `$C` binds to the matched `CONCEPT` node handle, not the enclosing `(CONCEPT ...)` link.
57
+
58
+ ## How a query is run
59
+
60
+ `queryPatternMatching` runs the whole exchange against a Query Agent in three steps.
61
+
62
+ It encodes the pattern as a prefix token stream (`query-tokens.ts`) using the DAS opcodes `LINK_TEMPLATE`, `LINK`, `NODE`, `VARIABLE`, and `ATOM`, the same stack machine as das-mono's `PatternMatchingQueryProcessor::setup_query_tree`. A link containing a variable is a `LINK_TEMPLATE`; a fully ground one is a `LINK`.
63
+
64
+ It issues the tokens as a `pattern_matching_query` with the `ServiceBus::issue_bus_command` framing, and hosts an inbound proxy node so the agent can stream answers back.
65
+
66
+ It decodes the streamed `answer_bundle` messages (`answer.ts`): it unwraps the `bus_command_proxy` envelope, then parses each `QueryAnswer` string into its variable assignment and matched handles.
67
+
68
+ Atom-handle hashing (`handle.ts`) is a port of `hyperon_das/hasher.py` and produces the same handles as the live AtomDB, which is what makes the handles in the query and in the decoded answers line up. The encoder and decoder have offline tests built from a captured answer, so the protocol stays covered with no DAS running.
69
+
70
+ ## Version matching
71
+
72
+ The released `1.0.0` Query Agent serves the `dasproto.AtomSpaceNode` service. A later `das-proto` renamed it to `DistributedAlgorithmNode`, and calling the new contract against the old agent returns gRPC `UNIMPLEMENTED`. The client carries both generated contracts, and the live path uses the one the running agent serves. Regenerate the stubs with `pnpm --filter @metta-ts/das-client gen` (needs `protoc`).
@@ -0,0 +1,223 @@
1
+ import { Atom, Bindings, Space } from '@metta-ts/core';
2
+ import { BinaryWriter, BinaryReader } from '@bufbuild/protobuf/wire';
3
+
4
+ interface DasTransport {
5
+ /** Pattern-matching query against the remote space; returns binding sets (DAS `query`). */
6
+ query(pattern: Atom): Bindings[];
7
+ /** Insert an atom through DAS `add_link` / atomdb write. */
8
+ add(atom: Atom): void;
9
+ remove(atom: Atom): boolean;
10
+ /** Enumerate atoms (where the backend supports it). */
11
+ atoms(): readonly Atom[];
12
+ }
13
+ /** In-process transport over a local atom list for tests and offline development.
14
+ * Uses the same `DasSpace`/grounded-op path as a bus client, without the network. */
15
+ declare class MockTransport implements DasTransport {
16
+ private readonly store;
17
+ constructor(store?: Atom[]);
18
+ query(pattern: Atom): Bindings[];
19
+ add(atom: Atom): void;
20
+ remove(atom: Atom): boolean;
21
+ atoms(): readonly Atom[];
22
+ }
23
+
24
+ declare class DasSpace implements Space {
25
+ private readonly transport;
26
+ constructor(transport: DasTransport);
27
+ add(atom: Atom): void;
28
+ remove(atom: Atom): boolean;
29
+ query(pattern: Atom): Bindings[];
30
+ atoms(): readonly Atom[];
31
+ }
32
+
33
+ declare function computeHash(input: string): string;
34
+ /** Hash of a named type (`named_type_hash`). */
35
+ declare function namedTypeHash(name: string): string;
36
+ /** Hash of a terminal node `(type, name)` (`terminal_hash`). */
37
+ declare function terminalHash(type: string, name: string): string;
38
+ /** Hash of a composite of element handles (`composite_hash`). */
39
+ declare function compositeHash(elements: readonly string[]): string;
40
+ /** Hash of an expression: the type hash followed by the element handles (`expression_hash`). */
41
+ declare function expressionHash(typeHash: string, elements: readonly string[]): string;
42
+
43
+ interface Ack {
44
+ error: boolean;
45
+ msg: string;
46
+ }
47
+ declare const Ack: MessageFns$1<Ack>;
48
+ type Builtin$1 = Date | Function | Uint8Array | string | number | boolean | undefined;
49
+ type DeepPartial$1<T> = T extends Builtin$1 ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial$1<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial$1<U>> : T extends {} ? {
50
+ [K in keyof T]?: DeepPartial$1<T[K]>;
51
+ } : Partial<T>;
52
+ type KeysOfUnion<T> = T extends T ? keyof T : never;
53
+ type Exact<P, I extends P> = P extends Builtin$1 ? P : P & {
54
+ [K in keyof P]: Exact<P[K], I[K]>;
55
+ } & {
56
+ [K in Exclude<keyof I, KeysOfUnion<P>>]: never;
57
+ };
58
+ interface MessageFns$1<T> {
59
+ encode(message: T, writer?: BinaryWriter): BinaryWriter;
60
+ decode(input: BinaryReader | Uint8Array, length?: number): T;
61
+ fromJSON(object: any): T;
62
+ toJSON(message: T): unknown;
63
+ create<I extends Exact<DeepPartial$1<T>, I>>(base?: I): T;
64
+ fromPartial<I extends Exact<DeepPartial$1<T>, I>>(object: I): T;
65
+ }
66
+
67
+ interface MessageData {
68
+ command: string;
69
+ args: string[];
70
+ sender: string;
71
+ isBroadcast: boolean;
72
+ visitedRecipients: string[];
73
+ }
74
+ declare const MessageData: MessageFns<MessageData>;
75
+ type Builtin = Date | Function | Uint8Array | string | number | boolean | undefined;
76
+ type DeepPartial<T> = T extends Builtin ? T : T extends globalThis.Array<infer U> ? globalThis.Array<DeepPartial<U>> : T extends ReadonlyArray<infer U> ? ReadonlyArray<DeepPartial<U>> : T extends {} ? {
77
+ [K in keyof T]?: DeepPartial<T[K]>;
78
+ } : Partial<T>;
79
+ interface MessageFns<T> {
80
+ encode(message: T, writer?: BinaryWriter): BinaryWriter;
81
+ decode(input: BinaryReader | Uint8Array, length?: number): T;
82
+ fromJSON(object: any): T;
83
+ toJSON(message: T): unknown;
84
+ create(base?: DeepPartial<T>): T;
85
+ fromPartial(object: DeepPartial<T>): T;
86
+ }
87
+
88
+ /** Bus command names from Python/Rust `service_bus` clients. */
89
+ declare const BusCommand: {
90
+ readonly ping: "ping";
91
+ readonly ack: "ack";
92
+ readonly nodeJoinedNetwork: "node_joined_network";
93
+ readonly busCommandProxy: "bus_command_proxy";
94
+ readonly queryAnswerTokensFlow: "query_answer_tokens_flow";
95
+ };
96
+ type MessageHandler = (msg: MessageData) => void;
97
+ /** Inbound gRPC node plus peer `execute_message` sender. */
98
+ declare class BusNode {
99
+ readonly address: string;
100
+ private readonly onMessage;
101
+ private server?;
102
+ constructor(address: string, onMessage?: MessageHandler);
103
+ /** Start the inbound gRPC server. Resolves once it is listening. */
104
+ start(): Promise<void>;
105
+ /** Send a command message to a peer node and await its acknowledgement. */
106
+ send(peer: string, message: MessageData): Promise<void>;
107
+ /** Ping a peer node; resolves with its `Ack`. */
108
+ ping(peer: string): Promise<Ack>;
109
+ stop(): Promise<void>;
110
+ }
111
+
112
+ type Pattern = {
113
+ kind: "node";
114
+ type: string;
115
+ name: string;
116
+ } | {
117
+ kind: "var";
118
+ name: string;
119
+ } | {
120
+ kind: "atom";
121
+ handle: string;
122
+ } | {
123
+ kind: "expr";
124
+ children: Pattern[];
125
+ };
126
+ /** A leaf symbol node, e.g. `EVALUATION` -> NODE Symbol EVALUATION. */
127
+ declare const node: (name: string, type?: string) => Pattern;
128
+ /** A query variable, e.g. `$C` -> VARIABLE C. */
129
+ declare const variable: (name: string) => Pattern;
130
+ /** An expression (link), e.g. `(CONCEPT $C)`. */
131
+ declare const expr: (...children: Pattern[]) => Pattern;
132
+ /** Emit the prefix token stream for a pattern (the `query` argument of a pattern_matching_query). */
133
+ declare function encodeQuery(p: Pattern, linkType?: string): string[];
134
+
135
+ declare const PROXY_COMMAND = "bus_command_proxy";
136
+ declare const ANSWER_BUNDLE = "answer_bundle";
137
+ declare const FINISHED = "finished";
138
+ declare const ABORT = "abort";
139
+ /** One query answer: variable bindings, matched link handles, and optional handle -> MeTTa text. */
140
+ interface QueryAnswer {
141
+ readonly assignment: Record<string, string>;
142
+ readonly handles: readonly string[];
143
+ readonly metta: Record<string, string>;
144
+ }
145
+ /** Unwrap a `bus_command_proxy` message into its inner peer command and arguments. */
146
+ declare function unwrapProxyMessage(args: readonly string[]): {
147
+ command: string;
148
+ args: string[];
149
+ };
150
+ /** Parse one `QueryAnswer::tokenize` string into assignment, matched handles, and optional handle -> MeTTa text.
151
+ * MeTTa values may contain spaces and parentheses, so scan with a cursor instead of splitting,
152
+ * matching das-mono QueryAnswer::untokenize (read_token + read_metta_expression). */
153
+ declare function parseQueryAnswer(token: string): QueryAnswer;
154
+ /** Fold received proxy messages into answers plus FINISHED/ABORT state.
155
+ * Only `bus_command_proxy` messages carry answers. */
156
+ declare function collectAnswers(messages: ReadonlyArray<{
157
+ command: string;
158
+ args: readonly string[];
159
+ }>): {
160
+ answers: QueryAnswer[];
161
+ finished: boolean;
162
+ aborted: boolean;
163
+ };
164
+
165
+ interface QueryOptions {
166
+ /** Host for the inbound proxy node (default `127.0.0.1`). The port is OS-assigned per query, so
167
+ * calls never collide on a port or reuse a bus node id. */
168
+ readonly proxyHost?: string;
169
+ /** Address of the live DAS Query Agent that owns `pattern_matching_query`. */
170
+ readonly agentAddress: string;
171
+ /** Query pattern; any nested variable makes it a template. */
172
+ readonly pattern: Pattern;
173
+ /** Optional query context string (defaults to empty). */
174
+ readonly context?: string;
175
+ /** Wait deadline for FINISHED/ABORT from the agent. */
176
+ readonly timeoutMs?: number;
177
+ /** Request handle -> MeTTa text mappings so bindings resolve without a separate AtomDB read. */
178
+ readonly populateMettaMapping?: boolean;
179
+ }
180
+ interface QueryResult {
181
+ readonly answers: QueryAnswer[];
182
+ readonly finished: boolean;
183
+ readonly aborted: boolean;
184
+ }
185
+ /**
186
+ * Issue a pattern-matching query against a live DAS Query Agent and return the decoded answers.
187
+ *
188
+ * Wire framing (das-mono ServiceBus::issue_bus_command + BaseQueryProxy::tokenize):
189
+ * command = "pattern_matching_query"
190
+ * args = [requestor_id, serial, requestor_proxy_node_id,
191
+ * numParamTokens, ...paramTokens, context, numQueryTokens, ...queryTokens]
192
+ * With no param tokens, the agent uses its configured defaults.
193
+ *
194
+ * The proxy node binds an OS-assigned port (`host:0`), so its bus node id is unique to this call.
195
+ * Concurrent and back-to-back queries avoid port collisions and the agent's "node already in the
196
+ * network" guard.
197
+ */
198
+ declare function queryPatternMatching(opts: QueryOptions): Promise<QueryResult>;
199
+
200
+ /** A knowledge store queried asynchronously (a remote/distributed AtomSpace). */
201
+ interface AsyncSpace {
202
+ /** All binding sets under which `pattern` matches a stored atom. */
203
+ queryAsync(pattern: Atom): Promise<Bindings[]>;
204
+ }
205
+ /** Convert a core MeTTa atom into a DAS query pattern. Symbols become `Symbol` nodes; string values
206
+ * become quoted `Symbol` nodes because DAS stores `"foo"` as a symbol name with quotes.
207
+ * Variables and expressions map recursively. Grounded error/unit/ext throw because they have no
208
+ * faithful DAS encoding. */
209
+ declare function atomToPattern(a: Atom): Pattern;
210
+ /** Live DAS async space. Each query hosts a proxy node for streamed answers and resolves bindings
211
+ * through the answer's MeTTa mapping. */
212
+ declare class DasLiveSpace implements AsyncSpace {
213
+ private readonly agentAddress;
214
+ private readonly timeoutMs;
215
+ private readonly proxyHost;
216
+ constructor(agentAddress: string, proxyHost?: string, timeoutMs?: number);
217
+ queryAsync(pattern: Atom): Promise<Bindings[]>;
218
+ }
219
+ /** Query the space, then instantiate `template` under each binding.
220
+ * Defaults to instantiating `pattern`. */
221
+ declare function matchAsync(space: AsyncSpace, pattern: Atom, template?: Atom): Promise<Atom[]>;
222
+
223
+ export { ABORT, ANSWER_BUNDLE, type AsyncSpace, BusCommand, BusNode, DasLiveSpace, DasSpace, type DasTransport, FINISHED, type MessageHandler, MockTransport, PROXY_COMMAND, type Pattern, type QueryAnswer, type QueryOptions, type QueryResult, atomToPattern, collectAnswers, compositeHash, computeHash, encodeQuery, expr, expressionHash, matchAsync, namedTypeHash, node, parseQueryAnswer, queryPatternMatching, terminalHash, unwrapProxyMessage, variable };
package/dist/index.js ADDED
@@ -0,0 +1,888 @@
1
+ // src/transport.ts
2
+ import { matchAtoms } from "@metta-ts/core";
3
+ var MockTransport = class {
4
+ constructor(store = []) {
5
+ this.store = store;
6
+ }
7
+ store;
8
+ query(pattern) {
9
+ const out = [];
10
+ for (const a of this.store) for (const b of matchAtoms(pattern, a)) out.push(b);
11
+ return out;
12
+ }
13
+ add(atom) {
14
+ this.store.push(atom);
15
+ }
16
+ remove(atom) {
17
+ const i = this.store.findIndex((a) => JSON.stringify(a) === JSON.stringify(atom));
18
+ if (i < 0) return false;
19
+ this.store.splice(i, 1);
20
+ return true;
21
+ }
22
+ atoms() {
23
+ return this.store;
24
+ }
25
+ };
26
+
27
+ // src/das-space.ts
28
+ import "@metta-ts/core";
29
+ var DasSpace = class {
30
+ constructor(transport) {
31
+ this.transport = transport;
32
+ }
33
+ transport;
34
+ add(atom) {
35
+ this.transport.add(atom);
36
+ }
37
+ remove(atom) {
38
+ return this.transport.remove(atom);
39
+ }
40
+ query(pattern) {
41
+ return this.transport.query(pattern);
42
+ }
43
+ atoms() {
44
+ return this.transport.atoms();
45
+ }
46
+ };
47
+
48
+ // src/handle.ts
49
+ import { createHash } from "crypto";
50
+ var JOINING_CHAR = " ";
51
+ var MAX_LITERAL_OR_SYMBOL_SIZE = 1e4;
52
+ var MAX_HASHABLE_STRING_SIZE = 1e5;
53
+ function computeHash(input) {
54
+ return createHash("md5").update(input, "utf8").digest("hex");
55
+ }
56
+ function namedTypeHash(name) {
57
+ return computeHash(name);
58
+ }
59
+ function terminalHash(type, name) {
60
+ if (type.length + name.length >= MAX_HASHABLE_STRING_SIZE)
61
+ throw new Error("Invalid (too large) terminal name");
62
+ return computeHash(`${type}${JOINING_CHAR}${name}`);
63
+ }
64
+ function compositeHash(elements) {
65
+ let total = 0;
66
+ for (const e of elements) {
67
+ if (e.length > MAX_LITERAL_OR_SYMBOL_SIZE)
68
+ throw new Error("Invalid (too large) composite elements");
69
+ total += e.length;
70
+ }
71
+ if (total >= MAX_HASHABLE_STRING_SIZE) throw new Error("Invalid (too large) composite elements");
72
+ return computeHash(elements.join(JOINING_CHAR));
73
+ }
74
+ function expressionHash(typeHash, elements) {
75
+ return compositeHash([typeHash, ...elements]);
76
+ }
77
+
78
+ // src/bus-node.ts
79
+ import {
80
+ Server,
81
+ ServerCredentials,
82
+ credentials
83
+ } from "@grpc/grpc-js";
84
+
85
+ // src/gen/distributed_algorithm_node.ts
86
+ import { BinaryReader as BinaryReader2, BinaryWriter as BinaryWriter2 } from "@bufbuild/protobuf/wire";
87
+ import {
88
+ makeGenericClientConstructor
89
+ } from "@grpc/grpc-js";
90
+
91
+ // src/gen/common.ts
92
+ import { BinaryReader, BinaryWriter } from "@bufbuild/protobuf/wire";
93
+ function createBaseEmpty() {
94
+ return {};
95
+ }
96
+ var Empty = {
97
+ encode(_, writer = new BinaryWriter()) {
98
+ return writer;
99
+ },
100
+ decode(input, length) {
101
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
102
+ const end = length === void 0 ? reader.len : reader.pos + length;
103
+ const message = createBaseEmpty();
104
+ while (reader.pos < end) {
105
+ const tag = reader.uint32();
106
+ switch (tag >>> 3) {
107
+ }
108
+ if ((tag & 7) === 4 || tag === 0) {
109
+ break;
110
+ }
111
+ reader.skip(tag & 7);
112
+ }
113
+ return message;
114
+ },
115
+ fromJSON(_) {
116
+ return {};
117
+ },
118
+ toJSON(_) {
119
+ const obj = {};
120
+ return obj;
121
+ },
122
+ create(base) {
123
+ return Empty.fromPartial(base ?? {});
124
+ },
125
+ fromPartial(_) {
126
+ const message = createBaseEmpty();
127
+ return message;
128
+ }
129
+ };
130
+ function createBaseAck() {
131
+ return { error: false, msg: "" };
132
+ }
133
+ var Ack = {
134
+ encode(message, writer = new BinaryWriter()) {
135
+ if (message.error !== false) {
136
+ writer.uint32(8).bool(message.error);
137
+ }
138
+ if (message.msg !== "") {
139
+ writer.uint32(18).string(message.msg);
140
+ }
141
+ return writer;
142
+ },
143
+ decode(input, length) {
144
+ const reader = input instanceof BinaryReader ? input : new BinaryReader(input);
145
+ const end = length === void 0 ? reader.len : reader.pos + length;
146
+ const message = createBaseAck();
147
+ while (reader.pos < end) {
148
+ const tag = reader.uint32();
149
+ switch (tag >>> 3) {
150
+ case 1: {
151
+ if (tag !== 8) {
152
+ break;
153
+ }
154
+ message.error = reader.bool();
155
+ continue;
156
+ }
157
+ case 2: {
158
+ if (tag !== 18) {
159
+ break;
160
+ }
161
+ message.msg = reader.string();
162
+ continue;
163
+ }
164
+ }
165
+ if ((tag & 7) === 4 || tag === 0) {
166
+ break;
167
+ }
168
+ reader.skip(tag & 7);
169
+ }
170
+ return message;
171
+ },
172
+ fromJSON(object) {
173
+ return {
174
+ error: isSet(object.error) ? globalThis.Boolean(object.error) : false,
175
+ msg: isSet(object.msg) ? globalThis.String(object.msg) : ""
176
+ };
177
+ },
178
+ toJSON(message) {
179
+ const obj = {};
180
+ if (message.error !== false) {
181
+ obj.error = message.error;
182
+ }
183
+ if (message.msg !== "") {
184
+ obj.msg = message.msg;
185
+ }
186
+ return obj;
187
+ },
188
+ create(base) {
189
+ return Ack.fromPartial(base ?? {});
190
+ },
191
+ fromPartial(object) {
192
+ const message = createBaseAck();
193
+ message.error = object.error ?? false;
194
+ message.msg = object.msg ?? "";
195
+ return message;
196
+ }
197
+ };
198
+ function isSet(value) {
199
+ return value !== null && value !== void 0;
200
+ }
201
+
202
+ // src/gen/distributed_algorithm_node.ts
203
+ function createBaseMessageData() {
204
+ return { command: "", args: [], sender: "", isBroadcast: false, visitedRecipients: [] };
205
+ }
206
+ var MessageData = {
207
+ encode(message, writer = new BinaryWriter2()) {
208
+ if (message.command !== "") {
209
+ writer.uint32(10).string(message.command);
210
+ }
211
+ for (const v of message.args) {
212
+ writer.uint32(18).string(v);
213
+ }
214
+ if (message.sender !== "") {
215
+ writer.uint32(26).string(message.sender);
216
+ }
217
+ if (message.isBroadcast !== false) {
218
+ writer.uint32(32).bool(message.isBroadcast);
219
+ }
220
+ for (const v of message.visitedRecipients) {
221
+ writer.uint32(42).string(v);
222
+ }
223
+ return writer;
224
+ },
225
+ decode(input, length) {
226
+ const reader = input instanceof BinaryReader2 ? input : new BinaryReader2(input);
227
+ const end = length === void 0 ? reader.len : reader.pos + length;
228
+ const message = createBaseMessageData();
229
+ while (reader.pos < end) {
230
+ const tag = reader.uint32();
231
+ switch (tag >>> 3) {
232
+ case 1: {
233
+ if (tag !== 10) {
234
+ break;
235
+ }
236
+ message.command = reader.string();
237
+ continue;
238
+ }
239
+ case 2: {
240
+ if (tag !== 18) {
241
+ break;
242
+ }
243
+ message.args.push(reader.string());
244
+ continue;
245
+ }
246
+ case 3: {
247
+ if (tag !== 26) {
248
+ break;
249
+ }
250
+ message.sender = reader.string();
251
+ continue;
252
+ }
253
+ case 4: {
254
+ if (tag !== 32) {
255
+ break;
256
+ }
257
+ message.isBroadcast = reader.bool();
258
+ continue;
259
+ }
260
+ case 5: {
261
+ if (tag !== 42) {
262
+ break;
263
+ }
264
+ message.visitedRecipients.push(reader.string());
265
+ continue;
266
+ }
267
+ }
268
+ if ((tag & 7) === 4 || tag === 0) {
269
+ break;
270
+ }
271
+ reader.skip(tag & 7);
272
+ }
273
+ return message;
274
+ },
275
+ fromJSON(object) {
276
+ return {
277
+ command: isSet2(object.command) ? globalThis.String(object.command) : "",
278
+ args: globalThis.Array.isArray(object?.args) ? object.args.map((e) => globalThis.String(e)) : [],
279
+ sender: isSet2(object.sender) ? globalThis.String(object.sender) : "",
280
+ isBroadcast: isSet2(object.isBroadcast) ? globalThis.Boolean(object.isBroadcast) : isSet2(object.is_broadcast) ? globalThis.Boolean(object.is_broadcast) : false,
281
+ visitedRecipients: globalThis.Array.isArray(object?.visitedRecipients) ? object.visitedRecipients.map((e) => globalThis.String(e)) : globalThis.Array.isArray(object?.visited_recipients) ? object.visited_recipients.map((e) => globalThis.String(e)) : []
282
+ };
283
+ },
284
+ toJSON(message) {
285
+ const obj = {};
286
+ if (message.command !== "") {
287
+ obj.command = message.command;
288
+ }
289
+ if (message.args?.length) {
290
+ obj.args = message.args;
291
+ }
292
+ if (message.sender !== "") {
293
+ obj.sender = message.sender;
294
+ }
295
+ if (message.isBroadcast !== false) {
296
+ obj.isBroadcast = message.isBroadcast;
297
+ }
298
+ if (message.visitedRecipients?.length) {
299
+ obj.visitedRecipients = message.visitedRecipients;
300
+ }
301
+ return obj;
302
+ },
303
+ create(base) {
304
+ return MessageData.fromPartial(base ?? {});
305
+ },
306
+ fromPartial(object) {
307
+ const message = createBaseMessageData();
308
+ message.command = object.command ?? "";
309
+ message.args = object.args?.map((e) => e) || [];
310
+ message.sender = object.sender ?? "";
311
+ message.isBroadcast = object.isBroadcast ?? false;
312
+ message.visitedRecipients = object.visitedRecipients?.map((e) => e) || [];
313
+ return message;
314
+ }
315
+ };
316
+ var DistributedAlgorithmNodeService = {
317
+ ping: {
318
+ path: "/dasproto.DistributedAlgorithmNode/ping",
319
+ requestStream: false,
320
+ responseStream: false,
321
+ requestSerialize: (value) => Buffer.from(Empty.encode(value).finish()),
322
+ requestDeserialize: (value) => Empty.decode(value),
323
+ responseSerialize: (value) => Buffer.from(Ack.encode(value).finish()),
324
+ responseDeserialize: (value) => Ack.decode(value)
325
+ },
326
+ executeMessage: {
327
+ path: "/dasproto.DistributedAlgorithmNode/execute_message",
328
+ requestStream: false,
329
+ responseStream: false,
330
+ requestSerialize: (value) => Buffer.from(MessageData.encode(value).finish()),
331
+ requestDeserialize: (value) => MessageData.decode(value),
332
+ responseSerialize: (value) => Buffer.from(Empty.encode(value).finish()),
333
+ responseDeserialize: (value) => Empty.decode(value)
334
+ }
335
+ };
336
+ var DistributedAlgorithmNodeClient = makeGenericClientConstructor(
337
+ DistributedAlgorithmNodeService,
338
+ "dasproto.DistributedAlgorithmNode"
339
+ );
340
+ function isSet2(value) {
341
+ return value !== null && value !== void 0;
342
+ }
343
+
344
+ // src/bus-node.ts
345
+ var BusCommand = {
346
+ ping: "ping",
347
+ ack: "ack",
348
+ nodeJoinedNetwork: "node_joined_network",
349
+ busCommandProxy: "bus_command_proxy",
350
+ queryAnswerTokensFlow: "query_answer_tokens_flow"
351
+ };
352
+ var BusNode = class {
353
+ constructor(address, onMessage = () => {
354
+ }) {
355
+ this.address = address;
356
+ this.onMessage = onMessage;
357
+ }
358
+ address;
359
+ onMessage;
360
+ server;
361
+ /** Start the inbound gRPC server. Resolves once it is listening. */
362
+ start() {
363
+ const server = new Server();
364
+ server.addService(DistributedAlgorithmNodeService, {
365
+ ping: (_call, cb) => cb(null, { error: false, msg: "pong" }),
366
+ executeMessage: (call, cb) => {
367
+ this.onMessage(call.request);
368
+ cb(null, {});
369
+ }
370
+ });
371
+ this.server = server;
372
+ return new Promise((resolve, reject) => {
373
+ server.bindAsync(
374
+ this.address,
375
+ ServerCredentials.createInsecure(),
376
+ (err) => err ? reject(err) : resolve()
377
+ );
378
+ });
379
+ }
380
+ /** Send a command message to a peer node and await its acknowledgement. */
381
+ send(peer, message) {
382
+ const client = new DistributedAlgorithmNodeClient(peer, credentials.createInsecure());
383
+ return new Promise((resolve, reject) => {
384
+ client.executeMessage(message, (err) => {
385
+ client.close();
386
+ if (err) reject(err);
387
+ else resolve();
388
+ });
389
+ });
390
+ }
391
+ /** Ping a peer node; resolves with its `Ack`. */
392
+ ping(peer) {
393
+ const client = new DistributedAlgorithmNodeClient(peer, credentials.createInsecure());
394
+ return new Promise((resolve, reject) => {
395
+ client.ping({}, (err, ack) => {
396
+ client.close();
397
+ if (err) reject(err);
398
+ else resolve(ack);
399
+ });
400
+ });
401
+ }
402
+ stop() {
403
+ return new Promise((resolve) => {
404
+ if (!this.server) return resolve();
405
+ this.server.tryShutdown(() => resolve());
406
+ });
407
+ }
408
+ };
409
+
410
+ // src/query-tokens.ts
411
+ var LinkSchema = {
412
+ ATOM: "ATOM",
413
+ NODE: "NODE",
414
+ LINK: "LINK",
415
+ UNTYPED_VARIABLE: "VARIABLE",
416
+ LINK_TEMPLATE: "LINK_TEMPLATE"
417
+ };
418
+ var node = (name, type = "Symbol") => ({ kind: "node", type, name });
419
+ var variable = (name) => ({ kind: "var", name });
420
+ var expr = (...children) => ({ kind: "expr", children });
421
+ function hasVar(p) {
422
+ switch (p.kind) {
423
+ case "var":
424
+ return true;
425
+ case "node":
426
+ case "atom":
427
+ return false;
428
+ case "expr":
429
+ return p.children.some(hasVar);
430
+ }
431
+ }
432
+ function encodeQuery(p, linkType = "Expression") {
433
+ switch (p.kind) {
434
+ case "node":
435
+ return [LinkSchema.NODE, p.type, p.name];
436
+ case "var":
437
+ return [LinkSchema.UNTYPED_VARIABLE, p.name];
438
+ case "atom":
439
+ return [LinkSchema.ATOM, p.handle];
440
+ case "expr": {
441
+ const head = hasVar(p) ? LinkSchema.LINK_TEMPLATE : LinkSchema.LINK;
442
+ const tokens = [head, linkType, String(p.children.length)];
443
+ for (const c of p.children) tokens.push(...encodeQuery(c, linkType));
444
+ return tokens;
445
+ }
446
+ }
447
+ }
448
+
449
+ // src/answer.ts
450
+ var PROXY_COMMAND = "bus_command_proxy";
451
+ var ANSWER_BUNDLE = "answer_bundle";
452
+ var FINISHED = "finished";
453
+ var ABORT = "abort";
454
+ function unwrapProxyMessage(args) {
455
+ if (args.length === 0) throw new Error("empty bus_command_proxy args");
456
+ return { command: args[args.length - 1], args: args.slice(0, -1) };
457
+ }
458
+ function parseQueryAnswer(token) {
459
+ const s = token;
460
+ let i = 0;
461
+ const word = (what) => {
462
+ while (i < s.length && s[i] === " ") i++;
463
+ const start = i;
464
+ while (i < s.length && s[i] !== " ") i++;
465
+ if (i === start)
466
+ throw new Error(`malformed query answer: expected ${what} at offset ${start} in <${s}>`);
467
+ return s.slice(start, i);
468
+ };
469
+ const count = (what) => {
470
+ const w = word(what);
471
+ const n = Number(w);
472
+ if (!Number.isInteger(n) || n < 0)
473
+ throw new Error(`malformed query answer: expected ${what} count, got <${w}> in <${s}>`);
474
+ return n;
475
+ };
476
+ const mettaExpr = () => {
477
+ while (i < s.length && s[i] === " ") i++;
478
+ const start = i;
479
+ if (s[i] === "(") {
480
+ let depth = 0;
481
+ let inStr = false;
482
+ let esc = false;
483
+ for (; i < s.length; i++) {
484
+ const c = s[i];
485
+ if (inStr) {
486
+ if (esc) esc = false;
487
+ else if (c === "\\") esc = true;
488
+ else if (c === '"') inStr = false;
489
+ } else if (c === '"') inStr = true;
490
+ else if (c === "(") depth++;
491
+ else if (c === ")" && --depth === 0) {
492
+ i++;
493
+ break;
494
+ }
495
+ }
496
+ if (depth !== 0)
497
+ throw new Error(`malformed query answer: unbalanced parens in metta expr <${s}>`);
498
+ return s.slice(start, i);
499
+ }
500
+ if (s[i] === '"') {
501
+ i++;
502
+ let esc = false;
503
+ let closed = false;
504
+ for (; i < s.length; i++) {
505
+ const c = s[i];
506
+ if (esc) esc = false;
507
+ else if (c === "\\") esc = true;
508
+ else if (c === '"') {
509
+ i++;
510
+ closed = true;
511
+ break;
512
+ }
513
+ }
514
+ if (!closed)
515
+ throw new Error(`malformed query answer: unterminated string in metta expr <${s}>`);
516
+ return s.slice(start, i);
517
+ }
518
+ return word("metta expr");
519
+ };
520
+ word("strength");
521
+ word("importance");
522
+ const handles = [];
523
+ const numHandles = count("handles");
524
+ for (let k = 0; k < numHandles; k++) handles.push(word("handle"));
525
+ const assignment = {};
526
+ const asgSize = count("assignment size");
527
+ for (let k = 0; k < asgSize; k++) {
528
+ const label = word("variable label");
529
+ assignment[label] = word("variable handle");
530
+ }
531
+ const metta = {};
532
+ const mettaSize = count("metta-map size");
533
+ for (let k = 0; k < mettaSize; k++) {
534
+ const handle = word("metta handle");
535
+ metta[handle] = mettaExpr();
536
+ }
537
+ return { assignment, handles, metta };
538
+ }
539
+ function collectAnswers(messages) {
540
+ const answers = [];
541
+ let finished = false;
542
+ let aborted = false;
543
+ for (const m of messages) {
544
+ if (m.command !== PROXY_COMMAND) continue;
545
+ const inner = unwrapProxyMessage(m.args);
546
+ switch (inner.command) {
547
+ case ANSWER_BUNDLE:
548
+ for (const tok of inner.args) answers.push(parseQueryAnswer(tok));
549
+ break;
550
+ case FINISHED:
551
+ finished = true;
552
+ break;
553
+ case ABORT:
554
+ aborted = true;
555
+ break;
556
+ default:
557
+ break;
558
+ }
559
+ }
560
+ return { answers, finished, aborted };
561
+ }
562
+
563
+ // src/query-client.ts
564
+ import { Server as Server2, ServerCredentials as ServerCredentials2, credentials as credentials2 } from "@grpc/grpc-js";
565
+
566
+ // src/gen/atom_space_node.ts
567
+ import { BinaryReader as BinaryReader3, BinaryWriter as BinaryWriter3 } from "@bufbuild/protobuf/wire";
568
+ import {
569
+ makeGenericClientConstructor as makeGenericClientConstructor2
570
+ } from "@grpc/grpc-js";
571
+ function createBaseMessageData2() {
572
+ return { command: "", args: [], sender: "", isBroadcast: false, visitedRecipients: [] };
573
+ }
574
+ var MessageData2 = {
575
+ encode(message, writer = new BinaryWriter3()) {
576
+ if (message.command !== "") {
577
+ writer.uint32(10).string(message.command);
578
+ }
579
+ for (const v of message.args) {
580
+ writer.uint32(18).string(v);
581
+ }
582
+ if (message.sender !== "") {
583
+ writer.uint32(26).string(message.sender);
584
+ }
585
+ if (message.isBroadcast !== false) {
586
+ writer.uint32(32).bool(message.isBroadcast);
587
+ }
588
+ for (const v of message.visitedRecipients) {
589
+ writer.uint32(42).string(v);
590
+ }
591
+ return writer;
592
+ },
593
+ decode(input, length) {
594
+ const reader = input instanceof BinaryReader3 ? input : new BinaryReader3(input);
595
+ const end = length === void 0 ? reader.len : reader.pos + length;
596
+ const message = createBaseMessageData2();
597
+ while (reader.pos < end) {
598
+ const tag = reader.uint32();
599
+ switch (tag >>> 3) {
600
+ case 1: {
601
+ if (tag !== 10) {
602
+ break;
603
+ }
604
+ message.command = reader.string();
605
+ continue;
606
+ }
607
+ case 2: {
608
+ if (tag !== 18) {
609
+ break;
610
+ }
611
+ message.args.push(reader.string());
612
+ continue;
613
+ }
614
+ case 3: {
615
+ if (tag !== 26) {
616
+ break;
617
+ }
618
+ message.sender = reader.string();
619
+ continue;
620
+ }
621
+ case 4: {
622
+ if (tag !== 32) {
623
+ break;
624
+ }
625
+ message.isBroadcast = reader.bool();
626
+ continue;
627
+ }
628
+ case 5: {
629
+ if (tag !== 42) {
630
+ break;
631
+ }
632
+ message.visitedRecipients.push(reader.string());
633
+ continue;
634
+ }
635
+ }
636
+ if ((tag & 7) === 4 || tag === 0) {
637
+ break;
638
+ }
639
+ reader.skip(tag & 7);
640
+ }
641
+ return message;
642
+ },
643
+ fromJSON(object) {
644
+ return {
645
+ command: isSet3(object.command) ? globalThis.String(object.command) : "",
646
+ args: globalThis.Array.isArray(object?.args) ? object.args.map((e) => globalThis.String(e)) : [],
647
+ sender: isSet3(object.sender) ? globalThis.String(object.sender) : "",
648
+ isBroadcast: isSet3(object.isBroadcast) ? globalThis.Boolean(object.isBroadcast) : isSet3(object.is_broadcast) ? globalThis.Boolean(object.is_broadcast) : false,
649
+ visitedRecipients: globalThis.Array.isArray(object?.visitedRecipients) ? object.visitedRecipients.map((e) => globalThis.String(e)) : globalThis.Array.isArray(object?.visited_recipients) ? object.visited_recipients.map((e) => globalThis.String(e)) : []
650
+ };
651
+ },
652
+ toJSON(message) {
653
+ const obj = {};
654
+ if (message.command !== "") {
655
+ obj.command = message.command;
656
+ }
657
+ if (message.args?.length) {
658
+ obj.args = message.args;
659
+ }
660
+ if (message.sender !== "") {
661
+ obj.sender = message.sender;
662
+ }
663
+ if (message.isBroadcast !== false) {
664
+ obj.isBroadcast = message.isBroadcast;
665
+ }
666
+ if (message.visitedRecipients?.length) {
667
+ obj.visitedRecipients = message.visitedRecipients;
668
+ }
669
+ return obj;
670
+ },
671
+ create(base) {
672
+ return MessageData2.fromPartial(base ?? {});
673
+ },
674
+ fromPartial(object) {
675
+ const message = createBaseMessageData2();
676
+ message.command = object.command ?? "";
677
+ message.args = object.args?.map((e) => e) || [];
678
+ message.sender = object.sender ?? "";
679
+ message.isBroadcast = object.isBroadcast ?? false;
680
+ message.visitedRecipients = object.visitedRecipients?.map((e) => e) || [];
681
+ return message;
682
+ }
683
+ };
684
+ var AtomSpaceNodeService = {
685
+ ping: {
686
+ path: "/dasproto.AtomSpaceNode/ping",
687
+ requestStream: false,
688
+ responseStream: false,
689
+ requestSerialize: (value) => Buffer.from(Empty.encode(value).finish()),
690
+ requestDeserialize: (value) => Empty.decode(value),
691
+ responseSerialize: (value) => Buffer.from(Ack.encode(value).finish()),
692
+ responseDeserialize: (value) => Ack.decode(value)
693
+ },
694
+ executeMessage: {
695
+ path: "/dasproto.AtomSpaceNode/execute_message",
696
+ requestStream: false,
697
+ responseStream: false,
698
+ requestSerialize: (value) => Buffer.from(MessageData2.encode(value).finish()),
699
+ requestDeserialize: (value) => MessageData2.decode(value),
700
+ responseSerialize: (value) => Buffer.from(Empty.encode(value).finish()),
701
+ responseDeserialize: (value) => Empty.decode(value)
702
+ }
703
+ };
704
+ var AtomSpaceNodeClient = makeGenericClientConstructor2(
705
+ AtomSpaceNodeService,
706
+ "dasproto.AtomSpaceNode"
707
+ );
708
+ function isSet3(value) {
709
+ return value !== null && value !== void 0;
710
+ }
711
+
712
+ // src/query-client.ts
713
+ function paramTokens(opts) {
714
+ const out = [];
715
+ if (opts.populateMettaMapping) out.push("populate_metta_mapping", "bool", "true");
716
+ return out;
717
+ }
718
+ var serialCounter = 0;
719
+ async function queryPatternMatching(opts) {
720
+ const { agentAddress, pattern, context = "", timeoutMs = 8e3 } = opts;
721
+ const proxyHost = opts.proxyHost ?? "127.0.0.1";
722
+ const received = [];
723
+ const server = new Server2();
724
+ server.addService(AtomSpaceNodeService, {
725
+ ping: (_c, cb) => cb(null, { error: false, msg: "PING" }),
726
+ executeMessage: (call, cb) => {
727
+ received.push({ command: call.request.command, args: call.request.args });
728
+ cb(null, {});
729
+ }
730
+ });
731
+ let bound = false;
732
+ try {
733
+ const port = await new Promise(
734
+ (res, rej) => server.bindAsync(
735
+ `${proxyHost}:0`,
736
+ ServerCredentials2.createInsecure(),
737
+ (e, p) => e ? rej(e) : res(p)
738
+ )
739
+ );
740
+ bound = true;
741
+ const proxyAddress = `${proxyHost}:${port}`;
742
+ const serial = String(serialCounter++);
743
+ const queryTokens = encodeQuery(pattern);
744
+ const params = paramTokens(opts);
745
+ const args = [
746
+ proxyAddress,
747
+ serial,
748
+ proxyAddress,
749
+ String(params.length),
750
+ ...params,
751
+ context,
752
+ String(queryTokens.length),
753
+ ...queryTokens
754
+ ];
755
+ const client = new AtomSpaceNodeClient(agentAddress, credentials2.createInsecure());
756
+ try {
757
+ await new Promise(
758
+ (res, rej) => client.executeMessage(
759
+ {
760
+ command: "pattern_matching_query",
761
+ args,
762
+ sender: proxyAddress,
763
+ isBroadcast: false,
764
+ visitedRecipients: []
765
+ },
766
+ (e) => e ? rej(e) : res()
767
+ )
768
+ );
769
+ } finally {
770
+ client.close();
771
+ }
772
+ const deadline = Date.now() + timeoutMs;
773
+ const done = () => received.some((m) => {
774
+ if (m.command !== PROXY_COMMAND) return false;
775
+ const c = unwrapProxyMessage(m.args).command;
776
+ return c === FINISHED || c === ABORT;
777
+ });
778
+ while (Date.now() < deadline && !done()) await new Promise((r) => setTimeout(r, 100));
779
+ } finally {
780
+ if (bound) await new Promise((r) => server.tryShutdown(() => r()));
781
+ else server.forceShutdown();
782
+ }
783
+ return collectAnswers(received);
784
+ }
785
+
786
+ // src/async-space.ts
787
+ import {
788
+ addValRaw,
789
+ emptyBindings,
790
+ instantiate,
791
+ parse,
792
+ standardTokenizer
793
+ } from "@metta-ts/core";
794
+ function escapeDasString(s) {
795
+ return s.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
796
+ }
797
+ function atomToPattern(a) {
798
+ switch (a.kind) {
799
+ case "sym":
800
+ return node(a.name);
801
+ case "var":
802
+ return variable(a.name);
803
+ case "expr":
804
+ return expr(...a.items.map(atomToPattern));
805
+ case "gnd": {
806
+ const v = a.value;
807
+ switch (v.g) {
808
+ case "str":
809
+ return node(`"${escapeDasString(v.s)}"`);
810
+ case "int":
811
+ case "float":
812
+ return node(String(v.n));
813
+ case "bool":
814
+ return node(v.b ? "True" : "False");
815
+ default:
816
+ throw new Error(
817
+ `atomToPattern: grounded value of kind '${v.g}' has no DAS query encoding`
818
+ );
819
+ }
820
+ }
821
+ }
822
+ }
823
+ var TK = standardTokenizer();
824
+ var DasLiveSpace = class {
825
+ constructor(agentAddress, proxyHost = "127.0.0.1", timeoutMs = 8e3) {
826
+ this.agentAddress = agentAddress;
827
+ this.timeoutMs = timeoutMs;
828
+ this.proxyHost = proxyHost.includes(":") ? proxyHost.slice(0, proxyHost.indexOf(":")) : proxyHost;
829
+ }
830
+ agentAddress;
831
+ timeoutMs;
832
+ proxyHost;
833
+ async queryAsync(pattern) {
834
+ const { answers, finished, aborted } = await queryPatternMatching({
835
+ proxyHost: this.proxyHost,
836
+ agentAddress: this.agentAddress,
837
+ pattern: atomToPattern(pattern),
838
+ populateMettaMapping: true,
839
+ timeoutMs: this.timeoutMs
840
+ });
841
+ if (aborted) throw new Error("DAS query aborted by the agent");
842
+ if (!finished)
843
+ throw new Error(`DAS query did not finish within ${this.timeoutMs}ms (incomplete results)`);
844
+ return answers.map((ans) => {
845
+ let b = emptyBindings;
846
+ for (const [label, handle] of Object.entries(ans.assignment)) {
847
+ const text = ans.metta[handle];
848
+ if (text === void 0)
849
+ throw new Error(`DAS answer has no MeTTa text for $${label} (handle ${handle})`);
850
+ const atom = parse(text, TK);
851
+ if (atom === void 0)
852
+ throw new Error(`DAS answer MeTTa text for $${label} did not parse: <${text}>`);
853
+ b = addValRaw(b, label, atom);
854
+ }
855
+ return b;
856
+ });
857
+ }
858
+ };
859
+ async function matchAsync(space, pattern, template = pattern) {
860
+ const bindings = await space.queryAsync(pattern);
861
+ return bindings.map((b) => instantiate(b, template));
862
+ }
863
+ export {
864
+ ABORT,
865
+ ANSWER_BUNDLE,
866
+ BusCommand,
867
+ BusNode,
868
+ DasLiveSpace,
869
+ DasSpace,
870
+ FINISHED,
871
+ MockTransport,
872
+ PROXY_COMMAND,
873
+ atomToPattern,
874
+ collectAnswers,
875
+ compositeHash,
876
+ computeHash,
877
+ encodeQuery,
878
+ expr,
879
+ expressionHash,
880
+ matchAsync,
881
+ namedTypeHash,
882
+ node,
883
+ parseQueryAnswer,
884
+ queryPatternMatching,
885
+ terminalHash,
886
+ unwrapProxyMessage,
887
+ variable
888
+ };
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@metta-ts/das-client",
3
+ "version": "1.0.0",
4
+ "license": "MIT",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist"
16
+ ],
17
+ "dependencies": {
18
+ "@grpc/grpc-js": "^1.14.4",
19
+ "@bufbuild/protobuf": "^2.2.3",
20
+ "@metta-ts/core": "1.0.0"
21
+ },
22
+ "devDependencies": {
23
+ "ts-proto": "^2.11.8"
24
+ },
25
+ "author": "MesTTo",
26
+ "engines": {
27
+ "node": ">=20"
28
+ },
29
+ "sideEffects": false,
30
+ "module": "./dist/index.js",
31
+ "publishConfig": {
32
+ "access": "public"
33
+ },
34
+ "description": "Client for SingularityNET Distributed AtomSpace (DAS) for MeTTa TS, over a Connect gateway.",
35
+ "keywords": [
36
+ "metta",
37
+ "hyperon",
38
+ "das",
39
+ "distributed-atomspace",
40
+ "singularitynet"
41
+ ],
42
+ "repository": {
43
+ "type": "git",
44
+ "url": "git+https://github.com/MesTTo/Meta-TypeScript-Talk.git",
45
+ "directory": "packages/das-client"
46
+ },
47
+ "homepage": "https://github.com/MesTTo/Meta-TypeScript-Talk#readme",
48
+ "bugs": {
49
+ "url": "https://github.com/MesTTo/Meta-TypeScript-Talk/issues"
50
+ },
51
+ "scripts": {
52
+ "build": "tsup src/index.ts --format esm --dts --clean",
53
+ "gen": "protoc --plugin=protoc-gen-ts_proto=./node_modules/.bin/protoc-gen-ts_proto --ts_proto_out=./src/gen --ts_proto_opt=outputServices=grpc-js,esModuleInterop=true,forceLong=string -I proto proto/*.proto"
54
+ }
55
+ }