@synapsor/client 0.1.3 → 0.1.5
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 +25 -0
- package/bin/synapsor.mjs +36 -10
- package/package.json +1 -1
- package/synapsor.mjs +72 -0
package/README.md
CHANGED
|
@@ -101,6 +101,30 @@ await run.complete({ decision: "waiver_proposed" }, { status: "waiting_approval"
|
|
|
101
101
|
const graph = await run.explain();
|
|
102
102
|
```
|
|
103
103
|
|
|
104
|
+
Find the persisted run/evidence/proposal later by business object, workflow,
|
|
105
|
+
tenant, query fingerprint, or time window:
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
const activity = await db.agentActivity.search({
|
|
109
|
+
tenantId: "acme",
|
|
110
|
+
businessObjectId: "T-1042",
|
|
111
|
+
workflow: "support.ticket_refund_flow",
|
|
112
|
+
timeRange: { from: 123, to: 456 },
|
|
113
|
+
limit: 50,
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
This helper sends the native lookup form:
|
|
118
|
+
|
|
119
|
+
```sql
|
|
120
|
+
SELECT *
|
|
121
|
+
FROM AGENT ACTIVITY
|
|
122
|
+
WHERE tenant_id = 'acme'
|
|
123
|
+
AND business_object_id = 'T-1042'
|
|
124
|
+
ORDER BY created_at DESC
|
|
125
|
+
LIMIT 50;
|
|
126
|
+
```
|
|
127
|
+
|
|
104
128
|
Replay a stored capability run by using the numeric `agent_run_id` returned by
|
|
105
129
|
Synapsor. Deterministic replay returns the captured persisted run; comparison
|
|
106
130
|
modes can inspect the original snapshot, current state, a commit version, a
|
|
@@ -163,6 +187,7 @@ and reports `applied`, `conflict`, or `failed` back to Synapsor idempotently.
|
|
|
163
187
|
- `setSession({...})`
|
|
164
188
|
- `invokeAgentCapability(name, args, options)`
|
|
165
189
|
- `agentRuns.start(...)`, `run.invokeCapability(...)`, `run.checkpoint(...)`, `run.complete(...)`, `run.explain(...)`
|
|
190
|
+
- `agentActivity.search({ tenantId, businessObjectId, workflow, timeRange, limit })`
|
|
166
191
|
- `replayAgentRun(id, { mode, version, timestamp, branchName })`
|
|
167
192
|
- `externalActions.claim(...)` and `externalActions.confirm(...)`
|
|
168
193
|
- `createAgentEval(...)` / `evals.create(...)` for run-history or `sourceTable` dataset evals
|
package/bin/synapsor.mjs
CHANGED
|
@@ -2,12 +2,14 @@
|
|
|
2
2
|
import { chmod, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { homedir } from "node:os";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
5
6
|
import { dirname, join } from "node:path";
|
|
6
7
|
import { fileURLToPath } from "node:url";
|
|
7
8
|
import { createInterface } from "node:readline/promises";
|
|
8
9
|
import { stdin as input, stdout as output } from "node:process";
|
|
9
10
|
|
|
10
|
-
const
|
|
11
|
+
const require = createRequire(import.meta.url);
|
|
12
|
+
const { version: PACKAGE_VERSION } = require("../package.json");
|
|
11
13
|
const DEFAULT_BASE_URL = "https://synapsor.ai";
|
|
12
14
|
const ERROR_HINTS = new Map([
|
|
13
15
|
["AUTH_REQUIRED", "Set SYNAPSOR_API_KEY or run `synapsor config set api-key`."],
|
|
@@ -170,6 +172,10 @@ function csvFlag(args, name) {
|
|
|
170
172
|
return value ? value.split(",").map((item) => item.trim()).filter(Boolean) : [];
|
|
171
173
|
}
|
|
172
174
|
|
|
175
|
+
function boolFlag(args, name) {
|
|
176
|
+
return args.includes(name);
|
|
177
|
+
}
|
|
178
|
+
|
|
173
179
|
function resolveSecretRef(value) {
|
|
174
180
|
const text = String(value || "").trim();
|
|
175
181
|
if (text.startsWith("env:")) {
|
|
@@ -280,24 +286,35 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
280
286
|
const envHint = kind === "mysql" ? "APP_MYSQL_URL" : "APP_POSTGRES_URL";
|
|
281
287
|
const name = flagValue(rest, "--name", defaultName);
|
|
282
288
|
const url = resolveSecretRef(flagValue(rest, "--url", ""));
|
|
289
|
+
const cdcUrl = resolveSecretRef(flagValue(rest, "--cdc-url", ""));
|
|
283
290
|
const ssl = flagValue(rest, "--ssl", "require");
|
|
284
|
-
const mode = flagValue(rest, "--mode", "
|
|
291
|
+
const mode = flagValue(rest, "--mode", "live_read");
|
|
285
292
|
const projectId = scopedProject(globals);
|
|
286
293
|
const databaseId = scopedDatabase(globals);
|
|
287
294
|
if (!projectId || !databaseId) throw new Error("PROJECT_AND_DATABASE_REQUIRED: use --project <project_id> --db <database_id> or SYNAPSOR_PROJECT_ID/SYNAPSOR_DATABASE_ID");
|
|
288
295
|
if (!url) throw new Error(`${kind.toUpperCase()}_URL_REQUIRED: usage \`synapsor sources create ${kind} --name ${defaultName} --url env:${envHint} --project <project> --db <database>\``);
|
|
289
|
-
|
|
296
|
+
const body = {
|
|
290
297
|
project_id: projectId,
|
|
291
298
|
database_id: databaseId,
|
|
292
299
|
kind,
|
|
293
300
|
name,
|
|
294
301
|
connection: { url, ssl_mode: ssl },
|
|
295
302
|
mode,
|
|
296
|
-
}
|
|
303
|
+
};
|
|
304
|
+
if (cdcUrl) body.cdc_connection = { url: cdcUrl, ssl_mode: ssl };
|
|
305
|
+
if (mode === "cdc_mirror" || boolFlag(rest, "--cdc-ack")) body.cdc_acknowledged = true;
|
|
306
|
+
return print(await request(globals, "POST", "/v1/control/external-sources", body), globals);
|
|
297
307
|
}
|
|
298
|
-
if (["test", "inspect", "generate", "doctor", "disable"].includes(sub)) {
|
|
308
|
+
if (["test", "inspect", "generate", "doctor", "disable", "cdc-status", "cdc-snapshot", "cdc-poll"].includes(sub)) {
|
|
299
309
|
const sourceId = await resolveSourceId(globals, third);
|
|
300
310
|
if (sub === "test") return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/test`, {}), globals);
|
|
311
|
+
if (sub === "cdc-status") return print(await request(globals, "GET", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/cdc/status`), globals);
|
|
312
|
+
if (sub === "cdc-snapshot" || sub === "cdc-poll") {
|
|
313
|
+
const tables = csvFlag(rest, "--tables");
|
|
314
|
+
const body = { cdc_acknowledged: true };
|
|
315
|
+
if (tables.length) body.tables = tables;
|
|
316
|
+
return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/cdc/${sub === "cdc-snapshot" ? "snapshot" : "poll"}`, body), globals);
|
|
317
|
+
}
|
|
301
318
|
if (sub === "inspect") {
|
|
302
319
|
const schema = flagValue(rest, "--schema", "public");
|
|
303
320
|
const database = flagValue(rest, "--database", "");
|
|
@@ -338,10 +355,10 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
338
355
|
const database = flagValue(rest, "--database", "");
|
|
339
356
|
const tables = csvFlag(rest, "--tables");
|
|
340
357
|
const tenantColumn = flagValue(rest, "--tenant-column", "");
|
|
341
|
-
const mode = flagValue(rest, "--mode", "
|
|
358
|
+
const mode = flagValue(rest, "--mode", "live_read");
|
|
342
359
|
if (!tables.length) throw new Error("TABLES_REQUIRED: pass --tables tickets,customers,policy_chunks");
|
|
343
360
|
if (!tenantColumn && !rest.includes("--single-tenant")) throw new Error("TENANT_COLUMN_REQUIRED: pass --tenant-column tenant_id or --single-tenant");
|
|
344
|
-
|
|
361
|
+
const body = {
|
|
345
362
|
tables: tables.map((table) => ({
|
|
346
363
|
...(database ? { database } : {}),
|
|
347
364
|
schema,
|
|
@@ -352,7 +369,9 @@ async function main(argv = process.argv.slice(2)) {
|
|
|
352
369
|
single_tenant: rest.includes("--single-tenant"),
|
|
353
370
|
mode,
|
|
354
371
|
})),
|
|
355
|
-
}
|
|
372
|
+
};
|
|
373
|
+
if (mode === "cdc_mirror" || boolFlag(rest, "--cdc-ack")) body.cdc_acknowledged = true;
|
|
374
|
+
return print(await request(globals, "POST", `/v1/control/external-sources/${encodeURIComponent(sourceId)}/import`, body), globals);
|
|
356
375
|
}
|
|
357
376
|
}
|
|
358
377
|
|
|
@@ -406,8 +425,8 @@ Usage:
|
|
|
406
425
|
synapsor projects create <name>
|
|
407
426
|
synapsor db list --project <project>
|
|
408
427
|
synapsor db create <name> --project <project>
|
|
409
|
-
synapsor sources create postgres --name app_postgres --url env:APP_POSTGRES_URL --ssl require --mode
|
|
410
|
-
synapsor sources create mysql --name app_mysql --url env:APP_MYSQL_URL --ssl require --mode
|
|
428
|
+
synapsor sources create postgres --name app_postgres --url env:APP_POSTGRES_URL --ssl require --mode live_read --project <project> --db <database>
|
|
429
|
+
synapsor sources create mysql --name app_mysql --url env:APP_MYSQL_URL --ssl require --mode live_read --project <project> --db <database>
|
|
411
430
|
synapsor sources test app_postgres --project <project>
|
|
412
431
|
synapsor sources inspect app_postgres --schema public --project <project>
|
|
413
432
|
synapsor sources inspect app_mysql --database shopdb --project <project>
|
|
@@ -428,6 +447,13 @@ Usage:
|
|
|
428
447
|
synapsor proposal reject <proposal-handle> --db <database> --yes
|
|
429
448
|
synapsor replay <run-id> --db <database>
|
|
430
449
|
|
|
450
|
+
Preview/dev CDC commands:
|
|
451
|
+
These are not the current public customer connector path. Use live_read unless you are running a reviewed dev/test CDC smoke.
|
|
452
|
+
synapsor sources import app_postgres --schema public --tables tickets --tenant-column tenant_id --mode cdc_mirror --cdc-ack --project <project>
|
|
453
|
+
synapsor sources cdc-snapshot app_postgres --tables external_support.tickets --project <project>
|
|
454
|
+
synapsor sources cdc-poll app_postgres --tables external_support.tickets --project <project>
|
|
455
|
+
synapsor sources cdc-status app_postgres --project <project>
|
|
456
|
+
|
|
431
457
|
Environment:
|
|
432
458
|
SYNAPSOR_API_KEY, SYNAPSOR_BASE_URL
|
|
433
459
|
|
package/package.json
CHANGED
package/synapsor.mjs
CHANGED
|
@@ -80,6 +80,7 @@ export class Synapsor {
|
|
|
80
80
|
this.apiKey = apiKey;
|
|
81
81
|
this.process = process;
|
|
82
82
|
this.timeoutMs = timeoutMs;
|
|
83
|
+
this.agentActivity = new AgentActivityNamespace(this);
|
|
83
84
|
this.agentRuns = new AgentRunsNamespace(this);
|
|
84
85
|
this.externalActions = new ExternalActionsNamespace(this);
|
|
85
86
|
this.evals = new AgentEvalsNamespace(this);
|
|
@@ -268,6 +269,13 @@ export class Synapsor {
|
|
|
268
269
|
return results.at(-1).result?.rows ?? [];
|
|
269
270
|
}
|
|
270
271
|
|
|
272
|
+
async searchAgentActivity(filters = {}, options = {}) {
|
|
273
|
+
return this.query(buildAgentActivitySearchSql(filters, options), {
|
|
274
|
+
session: options.session,
|
|
275
|
+
asOf: options.asOf ?? options.as_of,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
|
|
271
279
|
async invokeAgentCapability(capability, args = {}, {
|
|
272
280
|
session = undefined,
|
|
273
281
|
traceId = undefined,
|
|
@@ -1131,6 +1139,16 @@ export class Synapsor {
|
|
|
1131
1139
|
}
|
|
1132
1140
|
}
|
|
1133
1141
|
|
|
1142
|
+
class AgentActivityNamespace {
|
|
1143
|
+
constructor(client) {
|
|
1144
|
+
this.client = client;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
async search(filters = {}, options = {}) {
|
|
1148
|
+
return this.client.searchAgentActivity(filters, options);
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1134
1152
|
class AgentRunsNamespace {
|
|
1135
1153
|
constructor(client) {
|
|
1136
1154
|
this.client = client;
|
|
@@ -1396,6 +1414,60 @@ function typedSql(value) {
|
|
|
1396
1414
|
return `${identifier(ref.type)}(${sqlLiteral(String(ref.id ?? ""))})`;
|
|
1397
1415
|
}
|
|
1398
1416
|
|
|
1417
|
+
function buildAgentActivitySearchSql(filters = {}, options = {}) {
|
|
1418
|
+
const where = [];
|
|
1419
|
+
const addFilter = (column, ...keys) => {
|
|
1420
|
+
for (const key of keys) {
|
|
1421
|
+
if (filters[key] !== undefined && filters[key] !== null && filters[key] !== "") {
|
|
1422
|
+
where.push(`${column} = ${sqlScalar(filters[key])}`);
|
|
1423
|
+
return;
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
};
|
|
1427
|
+
addFilter("activity_kind", "activityKind", "activity_kind");
|
|
1428
|
+
addFilter("tenant_id", "tenantId", "tenant_id");
|
|
1429
|
+
addFilter("principal", "principal");
|
|
1430
|
+
addFilter("workflow", "workflow");
|
|
1431
|
+
addFilter("capability", "capability");
|
|
1432
|
+
addFilter("run_id", "runId", "run_id");
|
|
1433
|
+
addFilter("evidence_bundle_id", "evidenceBundleId", "evidence_bundle_id");
|
|
1434
|
+
addFilter("proposal_id", "proposalId", "proposal_id");
|
|
1435
|
+
addFilter("replay_id", "replayId", "replay_id");
|
|
1436
|
+
addFilter("business_object_type", "businessObjectType", "business_object_type");
|
|
1437
|
+
addFilter("business_object_id", "businessObjectId", "business_object_id");
|
|
1438
|
+
addFilter("query_fingerprint", "queryFingerprint", "query_fingerprint");
|
|
1439
|
+
|
|
1440
|
+
const timeRange = filters.timeRange ?? filters.time_range ?? {};
|
|
1441
|
+
const from = timeRange.from ?? filters.createdFrom ?? filters.created_from;
|
|
1442
|
+
const to = timeRange.to ?? filters.createdTo ?? filters.created_to;
|
|
1443
|
+
if (from !== undefined && from !== null && from !== "") {
|
|
1444
|
+
where.push(`created_at >= ${sqlScalar(from)}`);
|
|
1445
|
+
}
|
|
1446
|
+
if (to !== undefined && to !== null && to !== "") {
|
|
1447
|
+
where.push(`created_at <= ${sqlScalar(to)}`);
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
const requestedLimit = options.limit ?? filters.limit ?? 50;
|
|
1451
|
+
const limit = Number.isInteger(Number(requestedLimit)) && Number(requestedLimit) > 0
|
|
1452
|
+
? Math.min(Number(requestedLimit), 500)
|
|
1453
|
+
: 50;
|
|
1454
|
+
const whereSql = where.length > 0 ? ` WHERE ${where.join(" AND ")}` : "";
|
|
1455
|
+
return `SELECT * FROM AGENT ACTIVITY${whereSql} ORDER BY created_at DESC LIMIT ${limit};`;
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function sqlScalar(value) {
|
|
1459
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
1460
|
+
return String(Math.trunc(value));
|
|
1461
|
+
}
|
|
1462
|
+
if (typeof value === "bigint") {
|
|
1463
|
+
return String(value);
|
|
1464
|
+
}
|
|
1465
|
+
if (typeof value === "boolean") {
|
|
1466
|
+
return value ? "TRUE" : "FALSE";
|
|
1467
|
+
}
|
|
1468
|
+
return sqlLiteral(String(value));
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1399
1471
|
function sqlLiteral(value) {
|
|
1400
1472
|
return `'${String(value).replaceAll("'", "''")}'`;
|
|
1401
1473
|
}
|