@ziggs-ai/agent-sdk 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 +3 -1
- package/package.json +9 -4
- package/src/AgentHost.ts +495 -0
- package/src/adapters/OpenAIAdapter.ts +146 -0
- package/src/agent/Agent.ts +101 -0
- package/src/cognition/validateContext.ts +95 -0
- package/src/context/applyEffects.ts +80 -0
- package/src/context/batch.ts +17 -0
- package/src/context/classifyEnvelope.ts +38 -0
- package/src/context/routingLabels.ts +46 -0
- package/src/defineAgent.ts +62 -0
- package/src/formatters/AgreementFormatter.ts +111 -0
- package/src/formatters/HistoryFormatter.ts +166 -0
- package/src/formatters/index.ts +2 -0
- package/src/index.ts +105 -0
- package/src/ingress/normalizeIncoming.ts +162 -0
- package/src/memory/MemoryStore.ts +104 -0
- package/src/pricing/fleetDefaults.ts +218 -0
- package/src/pricing/fleetEvalFree.ts +24 -0
- package/src/pricing/fleetFreeTierA.gen.ts +12 -0
- package/src/pricing/fleetTierByAgentId.gen.ts +1022 -0
- package/src/runtime/AgentMachine.ts +364 -0
- package/src/runtime/PromptBuilder.ts +463 -0
- package/src/runtime/buildOutcome.ts +518 -0
- package/src/runtime/defaults.ts +75 -0
- package/src/runtime/runTurn.ts +691 -0
- package/src/runtime/validateWorkflow.ts +181 -0
- package/src/server/ConnectionPool.ts +155 -0
- package/src/server/EventQueue.ts +133 -0
- package/src/server/InboxCatchUp.ts +251 -0
- package/src/server/OutboxBuffer.ts +90 -0
- package/src/server/SeenMessages.ts +27 -0
- package/src/server/ZiggsEffectHandler.ts +409 -0
- package/src/server/agreements/AgreementService.ts +117 -0
- package/src/server/createHealthServer.ts +85 -0
- package/src/server/proactive/ProactiveTrigger.ts +83 -0
- package/src/server/runLauncher.ts +146 -0
- package/src/server/tasks/TaskService.ts +110 -0
- package/src/server/tasks/index.ts +1 -0
- package/src/server/telemetryIngest.ts +91 -0
- package/src/server/tools/index.ts +46 -0
- package/src/server/tools/tier1/protocolRunner.ts +133 -0
- package/src/server/tools/tier1/protocolTools.ts +99 -0
- package/src/server/tools/tier2/connectionTools.ts +75 -0
- package/src/server/tools/tier2/contextTools.ts +74 -0
- package/src/server/tools/tier2/discoveryTools.ts +34 -0
- package/src/server/tools/tier2/marketplaceTools.ts +25 -0
- package/src/server/tools/tier2/paymentTools.ts +193 -0
- package/src/server/ziggsconnect/ZiggsConnectClient.ts +126 -0
- package/src/server/ziggscontext/ZiggsContextClient.ts +137 -0
- package/src/server/ziggspay/ZiggsPayClient.ts +193 -0
- package/src/shared/ids.ts +3 -0
- package/src/shared/runtimeLog.ts +72 -0
- package/src/shared/types.ts +29 -0
- package/src/tasks/protocolRegistry.ts +25 -0
- package/src/tools/ToolManager.ts +95 -0
- package/src/tools/{ToolProvider.js → ToolProvider.ts} +5 -15
- package/src/tools/defineTool.ts +90 -0
- package/src/tools/index.ts +5 -0
- package/src/types.ts +407 -0
- package/src/utils/jsonExtractor.ts +100 -0
- package/src/ConnectionPool.js +0 -133
- package/src/adapters/OpenAIAdapter.js +0 -73
- package/src/agent/Agent.js +0 -121
- package/src/agent/EventQueue.js +0 -68
- package/src/agent/OutboxBuffer.js +0 -62
- package/src/cognition/PromptBuilder.js +0 -312
- package/src/cognition/resolveActionTool.js +0 -12
- package/src/cognition/runTurn.js +0 -578
- package/src/context/applyEffects.js +0 -133
- package/src/context/batch.js +0 -25
- package/src/context/classifyEnvelope.js +0 -82
- package/src/context/routingLabels.js +0 -54
- package/src/createHealthServer.js +0 -28
- package/src/formatters/HistoryFormatter.js +0 -257
- package/src/formatters/TaskFormatter.js +0 -180
- package/src/formatters/index.js +0 -9
- package/src/index.js +0 -76
- package/src/ingress/normalizeIncoming.js +0 -70
- package/src/runLauncher.js +0 -159
- package/src/shared/ids.js +0 -7
- package/src/shared/types.js +0 -86
- package/src/tasks/TaskService.js +0 -247
- package/src/tasks/index.js +0 -9
- package/src/tasks/taskCore.js +0 -229
- package/src/tasks/taskProtocolRegistry.js +0 -22
- package/src/tasks/taskProtocolRunner.js +0 -107
- package/src/tasks/taskProtocolTools.js +0 -87
- package/src/tools/ToolManager.js +0 -79
- package/src/tools/defineTool.js +0 -82
- package/src/tools/index.js +0 -11
- package/src/utils/jsonExtractor.js +0 -139
- package/src/workflow/AgentMachine.js +0 -250
- package/src/workflow/WorkflowRuntime.js +0 -63
- package/src/workflow/dsl.js +0 -287
- package/src/workflow/motifs.js +0 -435
- package/src/ziggs/runtime.js +0 -192
- /package/src/adapters/{index.js → index.ts} +0 -0
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { defineTool, type ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
|
+
import { ZiggsConnectClient } from '../../ziggsconnect/ZiggsConnectClient.js';
|
|
3
|
+
|
|
4
|
+
type Ctx = Record<string, unknown>;
|
|
5
|
+
type AnyObj = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
interface ConnectError extends Error {
|
|
8
|
+
status?: number;
|
|
9
|
+
body?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function clientFor(ctx: Ctx): ZiggsConnectClient {
|
|
13
|
+
const operatorKey = ctx?.['operatorKey'] as string | undefined;
|
|
14
|
+
if (!operatorKey) throw new Error('operatorKey missing from tool context');
|
|
15
|
+
return new ZiggsConnectClient({
|
|
16
|
+
operatorKey,
|
|
17
|
+
actAsAgentId: (ctx?.['agentId'] as string) || undefined,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function rethrowWithContext(error: unknown, prefix: string): never {
|
|
22
|
+
const e = error as ConnectError;
|
|
23
|
+
const wrapped = new Error(`${prefix}: ${e.message}`) as ConnectError;
|
|
24
|
+
if (e.status !== undefined) wrapped.status = e.status;
|
|
25
|
+
if (e.body !== undefined) wrapped.body = e.body;
|
|
26
|
+
(wrapped as unknown as AnyObj)['cause'] = error;
|
|
27
|
+
throw wrapped;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const connectionProxyTool: ToolDefinition = defineTool(
|
|
31
|
+
'connection_proxy',
|
|
32
|
+
{
|
|
33
|
+
connectionId: { type: 'string', required: true },
|
|
34
|
+
grantId: { type: 'string', required: true },
|
|
35
|
+
action: { type: 'string', required: true },
|
|
36
|
+
payload: { type: 'object' },
|
|
37
|
+
},
|
|
38
|
+
async (args, ctx) => {
|
|
39
|
+
if (!args['connectionId']) throw new Error('connectionId is required');
|
|
40
|
+
if (!args['grantId']) throw new Error('grantId is required');
|
|
41
|
+
if (!args['action']) throw new Error('action is required');
|
|
42
|
+
try {
|
|
43
|
+
const result = await clientFor(ctx as Ctx).proxy({
|
|
44
|
+
connectionId: args['connectionId'] as string,
|
|
45
|
+
grantId: args['grantId'] as string,
|
|
46
|
+
action: args['action'] as string,
|
|
47
|
+
payload: args['payload'],
|
|
48
|
+
});
|
|
49
|
+
return { status: 'ok', result };
|
|
50
|
+
} catch (e) {
|
|
51
|
+
rethrowWithContext(e, 'Connection proxy failed');
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
export const connectionListGrantsTool: ToolDefinition = defineTool(
|
|
57
|
+
'connection_list_grants',
|
|
58
|
+
{ connectionId: { type: 'string', required: true } },
|
|
59
|
+
async (args, ctx) => {
|
|
60
|
+
if (!args['connectionId']) throw new Error('connectionId is required');
|
|
61
|
+
try {
|
|
62
|
+
const grants = await clientFor(ctx as Ctx).listGrants({
|
|
63
|
+
connectionId: args['connectionId'] as string,
|
|
64
|
+
});
|
|
65
|
+
return { grants: grants || [] };
|
|
66
|
+
} catch (e) {
|
|
67
|
+
rethrowWithContext(e, 'Failed to list connection grants');
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
export const CONNECTION_TOOLS: ToolDefinition[] = [
|
|
73
|
+
connectionProxyTool,
|
|
74
|
+
connectionListGrantsTool,
|
|
75
|
+
];
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { defineTool, type ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
|
+
import { ZiggsContextClient } from '../../ziggscontext/ZiggsContextClient.js';
|
|
3
|
+
|
|
4
|
+
type Ctx = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
function clientFor(ctx: Ctx): ZiggsContextClient {
|
|
7
|
+
const operatorKey = ctx?.['operatorKey'] as string | undefined;
|
|
8
|
+
if (!operatorKey) throw new Error('operatorKey missing from tool context');
|
|
9
|
+
return new ZiggsContextClient({
|
|
10
|
+
operatorKey,
|
|
11
|
+
actAsAgentId: (ctx?.['agentId'] as string) || undefined,
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const contextDiscoverTool: ToolDefinition = defineTool(
|
|
16
|
+
'context_discover',
|
|
17
|
+
{},
|
|
18
|
+
async (_args, ctx) => clientFor(ctx as Ctx).discover(),
|
|
19
|
+
{
|
|
20
|
+
description:
|
|
21
|
+
'List which chats, agreements, and org scopes this agent can reach via context grants (descriptors only — no message or artifact content).',
|
|
22
|
+
},
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const contextListGrantsTool: ToolDefinition = defineTool(
|
|
26
|
+
'context_list_grants',
|
|
27
|
+
{},
|
|
28
|
+
async (_args, ctx) => clientFor(ctx as Ctx).listGrants(),
|
|
29
|
+
{
|
|
30
|
+
description:
|
|
31
|
+
'List context grants held by this agent (grant ids and bounds). Does not fetch scoped content.',
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
export const contextDelegateTool: ToolDefinition = defineTool(
|
|
36
|
+
'context_delegate',
|
|
37
|
+
{
|
|
38
|
+
parentGrantId: { type: 'string', required: true },
|
|
39
|
+
holderId: { type: 'string', required: true },
|
|
40
|
+
scopeKind: { type: 'string', required: true },
|
|
41
|
+
scopeId: { type: 'string', required: true },
|
|
42
|
+
temporal: { type: 'string', required: true },
|
|
43
|
+
expiresAt: 'string',
|
|
44
|
+
watermarkAt: 'string',
|
|
45
|
+
},
|
|
46
|
+
async (args, ctx) => {
|
|
47
|
+
const temporal = args['temporal'] as string;
|
|
48
|
+
if (temporal !== 'from-now' && temporal !== 'from-start') {
|
|
49
|
+
throw new Error('temporal must be from-now or from-start');
|
|
50
|
+
}
|
|
51
|
+
const scopeKind = args['scopeKind'] as string;
|
|
52
|
+
if (scopeKind !== 'chat' && scopeKind !== 'agreement' && scopeKind !== 'org') {
|
|
53
|
+
throw new Error('scopeKind must be chat, agreement, or org');
|
|
54
|
+
}
|
|
55
|
+
return clientFor(ctx as Ctx).delegate({
|
|
56
|
+
parentGrantId: args['parentGrantId'] as string,
|
|
57
|
+
holderId: args['holderId'] as string,
|
|
58
|
+
scope: { kind: scopeKind as 'chat' | 'agreement' | 'org', id: args['scopeId'] as string },
|
|
59
|
+
temporal,
|
|
60
|
+
expiresAt: (args['expiresAt'] as string | undefined) ?? undefined,
|
|
61
|
+
watermarkAt: args['watermarkAt'] as string | undefined,
|
|
62
|
+
});
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
description:
|
|
66
|
+
'Delegate a narrower child context grant (tighter scope, shorter expiry, or from-now). Holder-only.',
|
|
67
|
+
},
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
export const CONTEXT_TOOLS = [
|
|
71
|
+
contextDiscoverTool,
|
|
72
|
+
contextListGrantsTool,
|
|
73
|
+
contextDelegateTool,
|
|
74
|
+
] as const;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineTool, type ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
|
+
import { AgentSearchClient } from '@ziggs-ai/api-client';
|
|
3
|
+
|
|
4
|
+
type Ctx = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
function clientFor(ctx: Ctx): AgentSearchClient {
|
|
7
|
+
const operatorKey = ctx?.['operatorKey'] as string | undefined;
|
|
8
|
+
const agentId = ctx?.['agentId'] as string | undefined;
|
|
9
|
+
if (!operatorKey) throw new Error('operatorKey missing from tool context');
|
|
10
|
+
if (!agentId) throw new Error('agentId missing from tool context');
|
|
11
|
+
return new AgentSearchClient(operatorKey, agentId);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export const agentSearchTool: ToolDefinition = defineTool('agent_search',
|
|
15
|
+
{ query: { type: 'string', required: true }, limit: 'number' },
|
|
16
|
+
async (args, ctx) => {
|
|
17
|
+
if (!args['query']) throw new Error('query is required');
|
|
18
|
+
const options: { limit?: number } = {};
|
|
19
|
+
if (typeof args['limit'] === 'number') options.limit = args['limit'] as number;
|
|
20
|
+
return clientFor(ctx as Ctx).searchAgents(args['query'] as string, options);
|
|
21
|
+
},
|
|
22
|
+
{ description: 'Search for agents by capability, name, or description. Returns ranked results with relevance scores. Use before agreement_propose or agreement_subcontract to discover the right agent for a job.', isGenericFallback: true },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const agentGetTool: ToolDefinition = defineTool('agent_get',
|
|
26
|
+
{ agentId: { type: 'string', required: true } },
|
|
27
|
+
async (args, ctx) => {
|
|
28
|
+
if (!args['agentId']) throw new Error('agentId is required');
|
|
29
|
+
return clientFor(ctx as Ctx).getAgentById(args['agentId'] as string);
|
|
30
|
+
},
|
|
31
|
+
{ description: 'Fetch the full profile of a specific agent by ID. Use to confirm capabilities and terms before proposing an agreement.' },
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
export const DISCOVERY_TOOLS: ToolDefinition[] = [agentSearchTool, agentGetTool];
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { defineTool, type ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
|
+
import { pullFromLedger } from '@ziggs-ai/api-client';
|
|
3
|
+
|
|
4
|
+
type Ctx = Record<string, unknown>;
|
|
5
|
+
|
|
6
|
+
// Read-only marketplace access. Publishing an open quest is NOT here: it is the
|
|
7
|
+
// tier-1 protocol verb agreement_propose({ proposedTo: "everyone" }).
|
|
8
|
+
export const marketplaceViewTool: ToolDefinition = defineTool('marketplace_view',
|
|
9
|
+
{ limit: 'number', since: 'string' },
|
|
10
|
+
async (args, ctx) => {
|
|
11
|
+
const operatorKey = (ctx as Ctx)?.['operatorKey'] as string | undefined;
|
|
12
|
+
const agentId = (ctx as Ctx)?.['agentId'] as string | undefined;
|
|
13
|
+
if (!operatorKey) throw new Error('operatorKey missing from tool context');
|
|
14
|
+
if (!agentId) throw new Error('agentId missing from tool context');
|
|
15
|
+
const options: { limit?: number; since?: string } = {
|
|
16
|
+
limit: typeof args['limit'] === 'number' ? args['limit'] as number : 20,
|
|
17
|
+
};
|
|
18
|
+
if (args['since']) options.since = args['since'] as string;
|
|
19
|
+
const quests = await pullFromLedger(options, { operatorKey, agentId });
|
|
20
|
+
return { quests, count: quests.length };
|
|
21
|
+
},
|
|
22
|
+
{ description: 'Browse open quests on the marketplace — work that buyers have broadcast to any willing agent. Use to find available jobs. To claim a quest, use agreement_respond on the returned agreementId. To publish your own quest, use agreement_propose({ proposedTo: "everyone" }).' },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const MARKETPLACE_TOOLS: ToolDefinition[] = [marketplaceViewTool];
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { defineTool, type ToolDefinition } from '../../../tools/defineTool.js';
|
|
2
|
+
import { ZiggsPayClient } from '../../ziggspay/ZiggsPayClient.js';
|
|
3
|
+
|
|
4
|
+
type Ctx = Record<string, unknown>;
|
|
5
|
+
type AnyObj = Record<string, unknown>;
|
|
6
|
+
|
|
7
|
+
function clientFor(ctx: Ctx): ZiggsPayClient {
|
|
8
|
+
const operatorKey = ctx?.['operatorKey'] as string | undefined;
|
|
9
|
+
if (!operatorKey) throw new Error('operatorKey missing from tool context');
|
|
10
|
+
return new ZiggsPayClient({ operatorKey, actAsAgentId: (ctx?.['agentId'] as string) || undefined });
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface PayError extends Error { status?: number; body?: string; }
|
|
14
|
+
|
|
15
|
+
function rethrowWithContext(error: unknown, prefix: string): never {
|
|
16
|
+
const e = error as PayError;
|
|
17
|
+
const wrapped = new Error(`${prefix}: ${e.message}`) as PayError;
|
|
18
|
+
if (e.status !== undefined) wrapped.status = e.status;
|
|
19
|
+
if (e.body !== undefined) wrapped.body = e.body;
|
|
20
|
+
(wrapped as unknown as AnyObj)['cause'] = error;
|
|
21
|
+
throw wrapped;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function buildCaveats(args: AnyObj): AnyObj[] {
|
|
25
|
+
const caveats: AnyObj[] = [];
|
|
26
|
+
if (args['maxAmount'] != null) caveats.push({ type: 'max_amount', value: args['maxAmount'] });
|
|
27
|
+
if (args['dailyBudget'] != null) caveats.push({ type: 'daily_budget', value: args['dailyBudget'] });
|
|
28
|
+
if (args['allowedRecipients'] != null) caveats.push({ type: 'allowed_recipients', value: args['allowedRecipients'] });
|
|
29
|
+
if (args['expiresInSeconds'] != null) caveats.push({ type: 'expires_at', value: Date.now() + (args['expiresInSeconds'] as number) * 1000 });
|
|
30
|
+
return caveats;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export const paymentBalanceTool: ToolDefinition = defineTool('payment_balance', {},
|
|
34
|
+
async (_args, ctx) => {
|
|
35
|
+
try { return await clientFor(ctx as Ctx).balance(); }
|
|
36
|
+
catch (e) { rethrowWithContext(e, 'Failed to get balance'); }
|
|
37
|
+
},
|
|
38
|
+
{ description: "Check the caller's current wallet balance and available balance (total minus active holds). Use before a transfer to confirm sufficient funds." },
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
export const paymentTransferTool: ToolDefinition = defineTool('payment_transfer',
|
|
42
|
+
{ toWalletId: { type: 'string', required: true }, amount: { type: 'number', required: true }, description: 'string', idempotencyKey: 'string', paymentGrantId: 'string' },
|
|
43
|
+
async (args, ctx) => {
|
|
44
|
+
if (!args['toWalletId']) throw new Error('toWalletId is required');
|
|
45
|
+
if (!args['amount'] || (args['amount'] as number) <= 0) throw new Error('amount must be positive');
|
|
46
|
+
const client = clientFor(ctx as Ctx);
|
|
47
|
+
let result: AnyObj;
|
|
48
|
+
try {
|
|
49
|
+
result = await client.transfer({ to: args['toWalletId'] as string, amount: Math.round(args['amount'] as number), description: (args['description'] as string) || 'Agent-initiated transfer', idempotencyKey: args['idempotencyKey'] as string | undefined, paymentGrantId: args['paymentGrantId'] as string | undefined }) as AnyObj;
|
|
50
|
+
} catch (e) { rethrowWithContext(e, 'Transfer failed'); }
|
|
51
|
+
if (result!['status'] === 'approval_required') {
|
|
52
|
+
return { status: 'approval_required', approvalId: result!['approvalId'] || null, expiresAt: result!['expiresAt'] || null, reason: result!['reason'] || null, amount: args['amount'], toWalletId: result!['toWalletId'],
|
|
53
|
+
message: 'Transfer paused: the wallet owner must approve this amount.',
|
|
54
|
+
next_actions: [{ tool: 'payment_wait_for_approval', when: 'You expect a quick decision (≤2 min) and can wait inline.', args: { approvalId: result!['approvalId'], timeoutMs: 120000 } }, { tool: 'task_update_plan_step', when: 'You want to abandon the transfer and route around it.' }] };
|
|
55
|
+
}
|
|
56
|
+
return { status: 'transferred', transactionId: result!['transactionId'] || null, amount: args['amount'], toWalletId: result!['toWalletId'] };
|
|
57
|
+
},
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
export const paymentWaitForApprovalTool: ToolDefinition = defineTool('payment_wait_for_approval',
|
|
61
|
+
{ approvalId: { type: 'string', required: true }, timeoutMs: 'number', pollMs: 'number' },
|
|
62
|
+
async (args, ctx) => {
|
|
63
|
+
if (!args['approvalId']) throw new Error('approvalId is required');
|
|
64
|
+
try {
|
|
65
|
+
const result = await clientFor(ctx as Ctx).waitForApproval(args['approvalId'] as string, { timeoutMs: args['timeoutMs'] as number | undefined, pollMs: args['pollMs'] as number | undefined }) as AnyObj;
|
|
66
|
+
return { status: result['status'], approvalId: args['approvalId'], transactionId: result['transactionId'] || null, approval: result['approval'] || null };
|
|
67
|
+
} catch (e) { rethrowWithContext(e, 'wait_for_approval failed'); }
|
|
68
|
+
},
|
|
69
|
+
);
|
|
70
|
+
|
|
71
|
+
export const paymentResolveWalletTool: ToolDefinition = defineTool('payment_resolve_wallet',
|
|
72
|
+
{ userId: 'string', agentId: 'string' },
|
|
73
|
+
async (args, ctx) => {
|
|
74
|
+
if (!args['userId'] && !args['agentId']) throw new Error('Provide userId or agentId to resolve a wallet');
|
|
75
|
+
try {
|
|
76
|
+
const wallet = await clientFor(ctx as Ctx).resolve({ userId: args['userId'] as string | undefined, agentId: args['agentId'] as string | undefined }) as AnyObj | null;
|
|
77
|
+
return { walletId: wallet?.['walletId'] || null, ownerId: wallet?.['ownerId'] || null, currency: wallet?.['currency'] || 'pez', status: wallet?.['status'] || null };
|
|
78
|
+
} catch (e) { rethrowWithContext(e, 'Failed to resolve wallet'); }
|
|
79
|
+
},
|
|
80
|
+
{ description: 'Look up a walletId by userId or agentId. Use before a transfer when you only know the recipient by their platform ID.' },
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
export const paymentHoldTool: ToolDefinition = defineTool('payment_hold',
|
|
84
|
+
{ amount: { type: 'number', required: true }, description: 'string', idempotencyKey: 'string' },
|
|
85
|
+
async (args, ctx) => {
|
|
86
|
+
if (!args['amount'] || (args['amount'] as number) <= 0) throw new Error('amount must be positive');
|
|
87
|
+
try {
|
|
88
|
+
const result = await clientFor(ctx as Ctx).hold({ amount: Math.round(args['amount'] as number), description: (args['description'] as string) || 'Agent escrow hold', idempotencyKey: args['idempotencyKey'] as string | undefined }) as AnyObj;
|
|
89
|
+
return { status: 'held', transactionId: (result['transaction'] as AnyObj | undefined)?.['transactionId'] || null, amount: args['amount'] };
|
|
90
|
+
} catch (e) { rethrowWithContext(e, 'Hold failed'); }
|
|
91
|
+
},
|
|
92
|
+
{ description: 'Pre-authorize (escrow) funds without moving them. Use to reserve payment at agreement formation; release with payment_release once work is complete or to refund if work is cancelled.' },
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
export const paymentReleaseTool: ToolDefinition = defineTool('payment_release',
|
|
96
|
+
{ holdId: { type: 'string', required: true }, action: { type: 'string', required: true }, toWalletId: 'string', idempotencyKey: 'string' },
|
|
97
|
+
async (args, ctx) => {
|
|
98
|
+
if (!args['holdId']) throw new Error('holdId is required');
|
|
99
|
+
if (args['action'] !== 'complete' && args['action'] !== 'refund') throw new Error("action must be 'complete' or 'refund'");
|
|
100
|
+
if (args['action'] === 'complete' && !args['toWalletId']) throw new Error('toWalletId is required when action=complete');
|
|
101
|
+
try {
|
|
102
|
+
const result = await clientFor(ctx as Ctx).release({ holdId: args['holdId'] as string, action: args['action'] as string, toWalletId: args['toWalletId'] as string | undefined, idempotencyKey: args['idempotencyKey'] as string | undefined }) as AnyObj;
|
|
103
|
+
return { status: args['action'] === 'complete' ? 'settled' : 'refunded', transactionId: (result['transaction'] as AnyObj | undefined)?.['transactionId'] || null, holdId: args['holdId'], action: args['action'] };
|
|
104
|
+
} catch (e) { rethrowWithContext(e, 'Release failed'); }
|
|
105
|
+
},
|
|
106
|
+
{ description: "Settle or refund an escrow hold. Use action='complete' to transfer held funds to toWalletId (work done), or action='refund' to return funds to the sender (work cancelled)." },
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const issueGrantHandler = async (args: AnyObj, ctx: Ctx) => {
|
|
110
|
+
if (!args['holderId']) throw new Error('holderId is required');
|
|
111
|
+
try {
|
|
112
|
+
const caveats = buildCaveats(args);
|
|
113
|
+
const result = await clientFor(ctx).issueGrant({ holderId: args['holderId'] as string, caveats }) as AnyObj;
|
|
114
|
+
const grant = result['grant'] as AnyObj | undefined;
|
|
115
|
+
return { grantId: grant?.['grantId'] || null, holderId: grant?.['holderId'] || args['holderId'], caveats: grant?.['caveats'] || caveats, expiresAt: grant?.['expiresAt'] || null };
|
|
116
|
+
} catch (e) { rethrowWithContext(e, 'Failed to issue payment grant'); }
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const paymentIssueGrantTool: ToolDefinition = defineTool('payment_issue_grant',
|
|
120
|
+
{ holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' },
|
|
121
|
+
issueGrantHandler,
|
|
122
|
+
);
|
|
123
|
+
|
|
124
|
+
/** @deprecated Use payment_issue_grant */
|
|
125
|
+
export const paymentIssueTokenTool: ToolDefinition = defineTool('payment_issue_token',
|
|
126
|
+
{ holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' },
|
|
127
|
+
issueGrantHandler,
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
const attenuateGrantHandler = async (args: AnyObj, ctx: Ctx) => {
|
|
131
|
+
const grantId = (args['grantId'] || args['tokenId']) as string;
|
|
132
|
+
if (!grantId) throw new Error('grantId is required');
|
|
133
|
+
if (!args['holderId']) throw new Error('holderId is required');
|
|
134
|
+
try {
|
|
135
|
+
const caveats = buildCaveats(args);
|
|
136
|
+
const result = await clientFor(ctx).attenuateGrant({ grantId, holderId: args['holderId'] as string, caveats }) as AnyObj;
|
|
137
|
+
const grant = result['grant'] as AnyObj | undefined;
|
|
138
|
+
return { grantId: grant?.['grantId'] || null, parentGrantId: grant?.['parentGrantId'] || grantId, holderId: grant?.['holderId'] || args['holderId'], caveats: grant?.['caveats'] || caveats, expiresAt: grant?.['expiresAt'] || null };
|
|
139
|
+
} catch (e) { rethrowWithContext(e, 'Failed to attenuate payment grant'); }
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
export const paymentAttenuateGrantTool: ToolDefinition = defineTool('payment_attenuate_grant',
|
|
143
|
+
{ grantId: { type: 'string', required: true }, holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' },
|
|
144
|
+
attenuateGrantHandler,
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
/** @deprecated Use payment_attenuate_grant */
|
|
148
|
+
export const paymentAttenuateTokenTool: ToolDefinition = defineTool('payment_attenuate_token',
|
|
149
|
+
{ tokenId: { type: 'string', required: true }, holderId: { type: 'string', required: true }, maxAmount: 'number', dailyBudget: 'number', allowedRecipients: ['string'], expiresInSeconds: 'number' },
|
|
150
|
+
attenuateGrantHandler,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
const revokeGrantHandler = async (args: AnyObj, ctx: Ctx) => {
|
|
154
|
+
const grantId = (args['grantId'] || args['tokenId']) as string;
|
|
155
|
+
if (!grantId) throw new Error('grantId is required');
|
|
156
|
+
try {
|
|
157
|
+
const result = await clientFor(ctx).revokeGrant(grantId) as AnyObj;
|
|
158
|
+
return { status: 'revoked', grantId, revoked: result['revoked'] ?? null };
|
|
159
|
+
} catch (e) { rethrowWithContext(e, 'Failed to revoke payment grant'); }
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const paymentRevokeGrantTool: ToolDefinition = defineTool('payment_revoke_grant',
|
|
163
|
+
{ grantId: { type: 'string', required: true } },
|
|
164
|
+
revokeGrantHandler,
|
|
165
|
+
);
|
|
166
|
+
|
|
167
|
+
/** @deprecated Use payment_revoke_grant */
|
|
168
|
+
export const paymentRevokeTokenTool: ToolDefinition = defineTool('payment_revoke_token',
|
|
169
|
+
{ tokenId: { type: 'string', required: true } },
|
|
170
|
+
revokeGrantHandler,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
const listGrantsHandler = async (_args: AnyObj, ctx: Ctx) => {
|
|
174
|
+
try {
|
|
175
|
+
const result = await clientFor(ctx).listGrants();
|
|
176
|
+
return { grants: result || [] };
|
|
177
|
+
} catch (e) { rethrowWithContext(e, 'Failed to list payment grants'); }
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const paymentListGrantsTool: ToolDefinition = defineTool('payment_list_grants', {},
|
|
181
|
+
listGrantsHandler,
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
/** @deprecated Use payment_list_grants */
|
|
185
|
+
export const paymentListTokensTool: ToolDefinition = defineTool('payment_list_tokens', {},
|
|
186
|
+
listGrantsHandler,
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
export const PAYMENT_TOOLS: ToolDefinition[] = [
|
|
190
|
+
paymentBalanceTool, paymentTransferTool, paymentWaitForApprovalTool,
|
|
191
|
+
paymentHoldTool, paymentReleaseTool, paymentResolveWalletTool,
|
|
192
|
+
paymentIssueGrantTool, paymentAttenuateGrantTool, paymentRevokeGrantTool, paymentListGrantsTool,
|
|
193
|
+
];
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
|
|
3
|
+
function getDefaultBaseUrl(): string {
|
|
4
|
+
return process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || 'http://localhost:3000';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function parseError(text: string, fallback: string): string {
|
|
8
|
+
if (!text) return fallback;
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(text) as Record<string, unknown>;
|
|
11
|
+
return (parsed['message'] as string) || (parsed['error'] as string) || text;
|
|
12
|
+
} catch {
|
|
13
|
+
return text;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ConnectError extends Error {
|
|
18
|
+
status?: number;
|
|
19
|
+
body?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ZiggsConnectClientOptions {
|
|
23
|
+
operatorKey: string;
|
|
24
|
+
actAsAgentId?: string;
|
|
25
|
+
baseUrl?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ProxyParams {
|
|
29
|
+
connectionId: string;
|
|
30
|
+
grantId: string;
|
|
31
|
+
action: string;
|
|
32
|
+
payload?: unknown;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class ZiggsConnectClient {
|
|
36
|
+
private operatorKey: string;
|
|
37
|
+
private actAsAgentId: string | null;
|
|
38
|
+
private baseUrl: string;
|
|
39
|
+
|
|
40
|
+
constructor({ operatorKey, actAsAgentId, baseUrl }: ZiggsConnectClientOptions) {
|
|
41
|
+
if (!operatorKey || typeof operatorKey !== 'string') {
|
|
42
|
+
throw new Error('ZiggsConnectClient requires an operatorKey');
|
|
43
|
+
}
|
|
44
|
+
this.operatorKey = operatorKey;
|
|
45
|
+
this.actAsAgentId = actAsAgentId || null;
|
|
46
|
+
this.baseUrl = baseUrl || getDefaultBaseUrl();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Present a ConnectionGrant to the broker and perform a provider action.
|
|
51
|
+
* Returns the provider result — never the raw OAuth token.
|
|
52
|
+
*/
|
|
53
|
+
async proxy({ connectionId, grantId, action, payload }: ProxyParams): Promise<unknown> {
|
|
54
|
+
if (!connectionId) throw new Error('proxy: connectionId is required');
|
|
55
|
+
if (!grantId) throw new Error('proxy: grantId is required');
|
|
56
|
+
if (!action) throw new Error('proxy: action is required');
|
|
57
|
+
if (!this.actAsAgentId) {
|
|
58
|
+
throw new Error(
|
|
59
|
+
'proxy: actAsAgentId is required — connection broker calls must impersonate the grant holder agent',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
const res = (await this._post(`/connections/${encodeURIComponent(connectionId)}/proxy`, {
|
|
63
|
+
grantId,
|
|
64
|
+
action,
|
|
65
|
+
payload: payload ?? {},
|
|
66
|
+
})) as Record<string, unknown>;
|
|
67
|
+
return res['result'] ?? res;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* List ConnectionGrants held by this agent for a connection.
|
|
72
|
+
* Requires actAsAgentId; filters server rows to holderId === agent.
|
|
73
|
+
*/
|
|
74
|
+
async listGrants({ connectionId }: { connectionId: string }): Promise<unknown[]> {
|
|
75
|
+
if (!connectionId) throw new Error('listGrants: connectionId is required');
|
|
76
|
+
if (!this.actAsAgentId) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
'listGrants: actAsAgentId is required — grants are scoped to the impersonated agent',
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const res = (await this._get(
|
|
82
|
+
`/connections/${encodeURIComponent(connectionId)}/grants`,
|
|
83
|
+
)) as Record<string, unknown>;
|
|
84
|
+
const grants = (res['grants'] as Array<Record<string, unknown>>) || [];
|
|
85
|
+
return grants.filter((g) => g['holderId'] === this.actAsAgentId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
private _buildHeaders(extra: Record<string, string> = {}): Record<string, string> {
|
|
89
|
+
const h: Record<string, string> = { Authorization: `Bearer ${this.operatorKey}`, ...extra };
|
|
90
|
+
if (this.actAsAgentId) h['X-Agent-Id'] = this.actAsAgentId;
|
|
91
|
+
return h;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
private async _request(method: string, path: string, body: unknown): Promise<unknown> {
|
|
95
|
+
const init: RequestInit = {
|
|
96
|
+
method,
|
|
97
|
+
headers: this._buildHeaders(
|
|
98
|
+
body !== undefined ? { 'content-type': 'application/json' } : {},
|
|
99
|
+
),
|
|
100
|
+
};
|
|
101
|
+
if (body !== undefined) init.body = JSON.stringify(body);
|
|
102
|
+
const response = await fetch(`${this.baseUrl}${path}`, init);
|
|
103
|
+
const text = await response.text();
|
|
104
|
+
if (!response.ok) {
|
|
105
|
+
const err = new Error(parseError(text, `HTTP ${response.status}`)) as ConnectError;
|
|
106
|
+
err.status = response.status;
|
|
107
|
+
err.body = text;
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
return text ? JSON.parse(text) : null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private _get(path: string): Promise<unknown> {
|
|
114
|
+
return this._request('GET', path, undefined);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
private _post(path: string, body: unknown): Promise<unknown> {
|
|
118
|
+
return this._request('POST', path, body ?? {});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function createZiggsConnectClient(
|
|
123
|
+
opts: ZiggsConnectClientOptions,
|
|
124
|
+
): ZiggsConnectClient {
|
|
125
|
+
return new ZiggsConnectClient(opts);
|
|
126
|
+
}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
2
|
+
|
|
3
|
+
function getDefaultBaseUrl(): string {
|
|
4
|
+
return process.env.BACKEND_URL || process.env.ZIGGS_BACKEND_URL || 'http://localhost:3000';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function parseError(text: string, fallback: string): string {
|
|
8
|
+
if (!text) return fallback;
|
|
9
|
+
try {
|
|
10
|
+
const parsed = JSON.parse(text) as Record<string, unknown>;
|
|
11
|
+
return (parsed['message'] as string) || (parsed['error'] as string) || text;
|
|
12
|
+
} catch {
|
|
13
|
+
return text;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface ContextError extends Error {
|
|
18
|
+
status?: number;
|
|
19
|
+
body?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ZiggsContextClientOptions {
|
|
23
|
+
operatorKey: string;
|
|
24
|
+
actAsAgentId?: string;
|
|
25
|
+
baseUrl?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface ContextReachDescriptor {
|
|
29
|
+
grantId: string;
|
|
30
|
+
scope: { kind: 'chat' | 'agreement' | 'org'; id: string };
|
|
31
|
+
temporal: 'from-now' | 'from-start';
|
|
32
|
+
watermarkAt: string;
|
|
33
|
+
expiresAt: string | null;
|
|
34
|
+
parentGrantId: string | null;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface DelegateContextGrantParams {
|
|
39
|
+
parentGrantId: string;
|
|
40
|
+
holderId: string;
|
|
41
|
+
scope: { kind: 'chat' | 'agreement' | 'org'; id: string };
|
|
42
|
+
temporal: 'from-now' | 'from-start';
|
|
43
|
+
expiresAt?: string | null;
|
|
44
|
+
watermarkAt?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* HTTP client for context grants (ZIG-413).
|
|
49
|
+
* Presentation model (signed bytes vs grant id) is intentionally not baked in here.
|
|
50
|
+
*/
|
|
51
|
+
export class ZiggsContextClient {
|
|
52
|
+
private operatorKey: string;
|
|
53
|
+
private actAsAgentId: string | null;
|
|
54
|
+
private baseUrl: string;
|
|
55
|
+
|
|
56
|
+
constructor({ operatorKey, actAsAgentId, baseUrl }: ZiggsContextClientOptions) {
|
|
57
|
+
if (!operatorKey || typeof operatorKey !== 'string') {
|
|
58
|
+
throw new Error('ZiggsContextClient requires an operatorKey');
|
|
59
|
+
}
|
|
60
|
+
this.operatorKey = operatorKey;
|
|
61
|
+
this.actAsAgentId = actAsAgentId || null;
|
|
62
|
+
this.baseUrl = baseUrl || getDefaultBaseUrl();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** ZIG-412: scope descriptors for what this agent can reach (no content). */
|
|
66
|
+
async discover(): Promise<ContextReachDescriptor[]> {
|
|
67
|
+
this.requireActAsAgent('discover');
|
|
68
|
+
const res = (await this._get('/context/discovery')) as Record<string, unknown>;
|
|
69
|
+
return (res['reach'] as ContextReachDescriptor[]) || [];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/** List persisted grant rows held by this agent (may include signedBytes). */
|
|
73
|
+
async listGrants(): Promise<unknown[]> {
|
|
74
|
+
this.requireActAsAgent('listGrants');
|
|
75
|
+
const res = (await this._get('/context/grants')) as Record<string, unknown>;
|
|
76
|
+
const grants = (res['grants'] as Array<Record<string, unknown>>) || [];
|
|
77
|
+
return grants.filter((g) => g['holderId'] === this.actAsAgentId);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Holder delegates a narrower child grant. */
|
|
81
|
+
async delegate(params: DelegateContextGrantParams): Promise<unknown> {
|
|
82
|
+
this.requireActAsAgent('delegate');
|
|
83
|
+
if (!params.parentGrantId) throw new Error('delegate: parentGrantId is required');
|
|
84
|
+
if (!params.holderId) throw new Error('delegate: holderId is required');
|
|
85
|
+
if (!params.scope?.kind || !params.scope?.id) {
|
|
86
|
+
throw new Error('delegate: scope { kind, id } is required');
|
|
87
|
+
}
|
|
88
|
+
return this._post(`/context/grants/${encodeURIComponent(params.parentGrantId)}/delegate`, {
|
|
89
|
+
holderId: params.holderId,
|
|
90
|
+
scope: params.scope,
|
|
91
|
+
temporal: params.temporal,
|
|
92
|
+
expiresAt: params.expiresAt,
|
|
93
|
+
watermarkAt: params.watermarkAt,
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
private requireActAsAgent(method: string): void {
|
|
98
|
+
if (!this.actAsAgentId) {
|
|
99
|
+
throw new Error(
|
|
100
|
+
`${method}: actAsAgentId is required — context grant calls must impersonate the grant holder agent`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
private _buildHeaders(extra: Record<string, string> = {}): Record<string, string> {
|
|
106
|
+
const h: Record<string, string> = { Authorization: `Bearer ${this.operatorKey}`, ...extra };
|
|
107
|
+
if (this.actAsAgentId) h['X-Agent-Id'] = this.actAsAgentId;
|
|
108
|
+
return h;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
private async _request(method: string, path: string, body: unknown): Promise<unknown> {
|
|
112
|
+
const init: RequestInit = {
|
|
113
|
+
method,
|
|
114
|
+
headers: this._buildHeaders(
|
|
115
|
+
body !== undefined ? { 'content-type': 'application/json' } : {},
|
|
116
|
+
),
|
|
117
|
+
};
|
|
118
|
+
if (body !== undefined) init.body = JSON.stringify(body);
|
|
119
|
+
const res = await fetch(`${this.baseUrl}${path}`, init);
|
|
120
|
+
const text = await res.text();
|
|
121
|
+
if (!res.ok) {
|
|
122
|
+
const err = new Error(parseError(text, `${method} ${path} failed (${res.status})`)) as ContextError;
|
|
123
|
+
err.status = res.status;
|
|
124
|
+
err.body = text;
|
|
125
|
+
throw err;
|
|
126
|
+
}
|
|
127
|
+
return text ? JSON.parse(text) : {};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private _get(path: string): Promise<unknown> {
|
|
131
|
+
return this._request('GET', path, undefined);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
private _post(path: string, body: unknown): Promise<unknown> {
|
|
135
|
+
return this._request('POST', path, body);
|
|
136
|
+
}
|
|
137
|
+
}
|