@envarai/envarpay 2026.1027.1 → 2026.1027.2
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 +9 -1
- package/dist/adapters/configure-native.js +3 -2
- package/dist/connector/client.d.ts +15 -2
- package/dist/connector/client.js +61 -0
- package/dist/connector/contracts.generated.d.ts +49 -1
- package/dist/connector/contracts.generated.js +1 -1
- package/dist/connector/mcp.js +50 -1
- package/dist/connector/state.d.ts +2 -0
- package/dist/connector/state.js +18 -0
- package/package.json +1 -1
- package/schemas/agent-work.v1.schema.json +247 -0
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ envarpay connector status --agent AGENT_UUID
|
|
|
21
21
|
envarpay connector disconnect --agent AGENT_UUID
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
The installed native MCP tools send free tasks to explicitly permitted recipients and retrieve original results. They grant no wallet access. The Connector journals claims, admission, native task references and pending reports. A lost native start response remains unknown and is never automatically resubmitted. Preserve the original private directory when recovering.
|
|
24
|
+
The installed native MCP tools send free tasks to explicitly permitted recipients and retrieve original results. They also discover text workflow services, create original quotes and confirm purchases only under a separate owner-approved service budget. They grant no account-wide wallet access. The Connector journals claims, admission, native task references and pending reports. A lost native start response remains unknown and is never automatically resubmitted. Preserve the original private directory when recovering.
|
|
25
25
|
|
|
26
26
|
After disconnecting in Envar, stop the old receiver and run the original `connect` command with `--new-pairing`, then approve the fresh code. Local `disconnect` already makes the next `connect` request fresh approval. Old journals remain intact. This is a new connection, not recovery of previously admitted work.
|
|
27
27
|
|
|
@@ -44,3 +44,11 @@ envarpay inspect --state /private/envarpay/seller.sqlite3
|
|
|
44
44
|
The seller configuration schema is [`schemas/envarpay.config.schema.json`](schemas/envarpay.config.schema.json). Paid amounts are integer **micro-USD** (`100000` = USD 0.10); free offers have no payment profile. An `envar_balance` profile contains only `{ "adapter": "envar_balance", "currency": "usd", "accountRef": "envar" }`. The private Envar policy must allow that exact account reference.
|
|
45
45
|
|
|
46
46
|
For development, run `npm ci --ignore-scripts`, `npm run build`, and `npm test` from this directory. Node.js 22.14+ is required; Node 24 is recommended.
|
|
47
|
+
|
|
48
|
+
## Agent service orders
|
|
49
|
+
|
|
50
|
+
Requires the companion Envar commerce deployment. `list_services` returns the exact published revision and price. `quote_service` creates one original quote without executing or paying. Keep its request UUID; a lost response is recovered with that same UUID and input. `list_orders` recovers the Agent's persisted quotes/orders after a restart.
|
|
51
|
+
|
|
52
|
+
The owner creates an Envar spending grant for this Agent, exact service revision, per-order and total amount, and expiry. `list_spending_grants` reads only this Agent's grants. `confirm_order` reserves the original amount under that grant; without a grant it returns the first-party link for owner confirmation of the same quote. Seller admission captures once. Use `wait_order`/`get_order` and the original work ID until the actual result arrives. `recover_order` cannot create a replacement purchase. There is no top-up, withdrawal, merchant or account-management tool.
|
|
53
|
+
|
|
54
|
+
Updating the package changes the native MCP tool set. Rerun the original connection command and follow its gateway restart guidance; setup detects a package-version change even when the MCP executable path stayed the same.
|
|
@@ -9,6 +9,7 @@ import JSON5 from 'json5';
|
|
|
9
9
|
import { parse as parseYaml, stringify as stringifyYaml } from 'yaml';
|
|
10
10
|
import { digest, validateUrl } from '../commerce/config.js';
|
|
11
11
|
import { privateJson, privateText, writePrivate, writePrivateJson } from '../connector/files.js';
|
|
12
|
+
const connectorVersion = JSON.parse(readFileSync(new URL('../../package.json', import.meta.url), 'utf8')).version;
|
|
12
13
|
function object(value) {
|
|
13
14
|
if (value === undefined || value === null)
|
|
14
15
|
return {};
|
|
@@ -201,6 +202,6 @@ export function configureNative(directory, config) {
|
|
|
201
202
|
const backupRoot = join(directory, 'native-backups');
|
|
202
203
|
const envChanged = writeProfile(envPath, changeEnv(envText, changes), backupRoot);
|
|
203
204
|
const configChanged = writeProfile(path, yaml ? stringifyYaml(value) : JSON.stringify(value, null, 2) + '\n', backupRoot);
|
|
204
|
-
writePrivateJson(markerFile, { agentId: config.agentId, home, mcpDigest: digest(mcp) });
|
|
205
|
-
return envChanged || configChanged;
|
|
205
|
+
writePrivateJson(markerFile, { agentId: config.agentId, home, mcpDigest: digest(mcp), connectorVersion });
|
|
206
|
+
return envChanged || configChanged || previous?.connectorVersion !== connectorVersion;
|
|
206
207
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Claim, ExecutionRef, Inbox, Report, Submit, Work } from './contracts.generated.js';
|
|
1
|
+
import type { Claim, ConfirmOrderResult, ExecutionRef, Inbox, Order, Report, ServiceConfirmation, ServiceQuoteRequest, Submit, Work } from './contracts.generated.js';
|
|
2
2
|
export declare function validate<T>(kind: string, value: unknown): T;
|
|
3
3
|
export declare class WorkApiError extends Error {
|
|
4
4
|
readonly status: number;
|
|
@@ -7,7 +7,7 @@ export declare class WorkApiError extends Error {
|
|
|
7
7
|
constructor(status: number, code: string, retryAfter?: number);
|
|
8
8
|
}
|
|
9
9
|
export interface ClaimedWork extends Work {
|
|
10
|
-
claim
|
|
10
|
+
claim?: Claim & {
|
|
11
11
|
lease_until: string | null;
|
|
12
12
|
};
|
|
13
13
|
execution_ref: Partial<ExecutionRef>;
|
|
@@ -56,4 +56,17 @@ export declare class EnvarWorkClient {
|
|
|
56
56
|
recover(workId: string, claim: Claim, execution_ref: ExecutionRef): Promise<ClaimedWork>;
|
|
57
57
|
report(workId: string, report: Report): Promise<Work>;
|
|
58
58
|
acknowledge(deliveryId: number): Promise<void>;
|
|
59
|
+
services(query?: string, serviceId?: string): Promise<{
|
|
60
|
+
results: Record<string, unknown>[];
|
|
61
|
+
}>;
|
|
62
|
+
spendingGrants(): Promise<{
|
|
63
|
+
items: Record<string, unknown>[];
|
|
64
|
+
}>;
|
|
65
|
+
quoteService(request: ServiceQuoteRequest): Promise<Order>;
|
|
66
|
+
order(orderId: string): Promise<Order>;
|
|
67
|
+
orders(): Promise<{
|
|
68
|
+
items: Order[];
|
|
69
|
+
}>;
|
|
70
|
+
confirmOrder(orderId: string, confirmation: ServiceConfirmation): Promise<ConfirmOrderResult>;
|
|
71
|
+
recoverOrder(orderId: string): Promise<Order>;
|
|
59
72
|
}
|
package/dist/connector/client.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { Ajv2020 } from 'ajv/dist/2020.js';
|
|
4
4
|
import addFormatsImport from 'ajv-formats';
|
|
5
|
+
import { digest } from '../commerce/config.js';
|
|
5
6
|
const ajv = new Ajv2020({ allErrors: false, strict: true });
|
|
6
7
|
addFormatsImport(ajv);
|
|
7
8
|
const schema = JSON.parse(readFileSync(new URL('../../schemas/agent-work.v1.schema.json', import.meta.url), 'utf8'));
|
|
@@ -120,6 +121,10 @@ export class EnvarWorkClient {
|
|
|
120
121
|
const result = await this.request(path, body, requestId);
|
|
121
122
|
const { claim, execution_ref, replayed, ...work } = result;
|
|
122
123
|
validate('work', work);
|
|
124
|
+
if (path.endsWith('/claim') && ['completed', 'failed', 'canceled'].includes(work.status) && !uuid.test(claim?.id ?? ''))
|
|
125
|
+
return { ...work, execution_ref, ...(replayed === undefined ? {} : { replayed }) };
|
|
126
|
+
if (!claim)
|
|
127
|
+
throw new Error('The active original work has no claim');
|
|
123
128
|
validate('claim', { id: claim.id, fence: claim.fence });
|
|
124
129
|
return { ...work, claim, execution_ref, ...(replayed === undefined ? {} : { replayed }) };
|
|
125
130
|
}
|
|
@@ -143,4 +148,60 @@ export class EnvarWorkClient {
|
|
|
143
148
|
throw new Error('Invalid delivery ID');
|
|
144
149
|
await this.request(`/api/v1/inbox/${deliveryId}/ack`, {});
|
|
145
150
|
}
|
|
151
|
+
async services(query = '', serviceId) {
|
|
152
|
+
if (query.length > 120)
|
|
153
|
+
throw new Error('Use a bounded service query');
|
|
154
|
+
const params = new URLSearchParams({ q: query });
|
|
155
|
+
if (serviceId)
|
|
156
|
+
params.set('service', id(serviceId));
|
|
157
|
+
const value = await this.request('/api/v1/agent-commerce/services?' + params);
|
|
158
|
+
if (!Array.isArray(value?.results) || value.results.length > 100)
|
|
159
|
+
throw new Error('Invalid service directory');
|
|
160
|
+
return value;
|
|
161
|
+
}
|
|
162
|
+
async spendingGrants() {
|
|
163
|
+
const value = await this.request('/api/v1/agent-commerce/spending-grants');
|
|
164
|
+
if (!Array.isArray(value?.items) || value.items.length > 100)
|
|
165
|
+
throw new Error('Invalid Agent budget response');
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
168
|
+
async quoteService(request) {
|
|
169
|
+
validate('service_quote_request', request);
|
|
170
|
+
const order = validate('order', await this.request('/api/v1/agent-commerce/quotes', request, request.message_id));
|
|
171
|
+
if (order.service_id !== request.service_id || order.message_id !== request.message_id || order.offer_id !== request.offer_id ||
|
|
172
|
+
order.payment_method !== request.payment_method || order.quote.input_digest !== digest(request.input))
|
|
173
|
+
throw new Error('The service quote differs from the original request');
|
|
174
|
+
return order;
|
|
175
|
+
}
|
|
176
|
+
async order(orderId) {
|
|
177
|
+
const order = validate('order', await this.request(`/api/v1/agent-commerce/orders/${id(orderId)}`));
|
|
178
|
+
if (order.id !== orderId)
|
|
179
|
+
throw new Error('Envar returned a different original order');
|
|
180
|
+
return order;
|
|
181
|
+
}
|
|
182
|
+
async orders() {
|
|
183
|
+
const value = await this.request('/api/v1/agent-commerce/orders');
|
|
184
|
+
if (!Array.isArray(value?.items) || value.items.length > 100)
|
|
185
|
+
throw new Error('Invalid original-order list');
|
|
186
|
+
return { items: value.items.map(item => validate('order', item)) };
|
|
187
|
+
}
|
|
188
|
+
async confirmOrder(orderId, confirmation) {
|
|
189
|
+
validate('service_confirmation', confirmation);
|
|
190
|
+
const receipt = validate('confirm_order_result', await this.request(`/api/v1/agent-commerce/orders/${id(orderId)}/confirm`, confirmation, orderId));
|
|
191
|
+
if (receipt.order.id !== orderId || receipt.order.message_id !== confirmation.message_id)
|
|
192
|
+
throw new Error('Confirmation returned another order');
|
|
193
|
+
if (receipt.approval_required) {
|
|
194
|
+
const approval = new URL(receipt.approval_url ?? '');
|
|
195
|
+
if (approval.origin !== this.origin || approval.pathname !== `/console/orders/${orderId}` ||
|
|
196
|
+
approval.username || approval.password || approval.search || approval.hash)
|
|
197
|
+
throw new Error('Owner approval must remain on the original Envar order');
|
|
198
|
+
}
|
|
199
|
+
return receipt;
|
|
200
|
+
}
|
|
201
|
+
async recoverOrder(orderId) {
|
|
202
|
+
const order = validate('order', await this.request(`/api/v1/agent-commerce/orders/${id(orderId)}/recover`, {}, orderId));
|
|
203
|
+
if (order.id !== orderId)
|
|
204
|
+
throw new Error('Recovery returned another order');
|
|
205
|
+
return order;
|
|
206
|
+
}
|
|
146
207
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare const WORK_SCHEMA_SHA256 = "
|
|
1
|
+
export declare const WORK_SCHEMA_SHA256 = "13142409150be2625fa6e8d2b4943a6714eeb7f17eb0d423e82140851051f290";
|
|
2
2
|
export type Claim = {
|
|
3
3
|
"id": string;
|
|
4
4
|
"fence": number;
|
|
@@ -63,3 +63,51 @@ export type Inbox = {
|
|
|
63
63
|
"items": Array<Delivery>;
|
|
64
64
|
"next_cursor": number | null;
|
|
65
65
|
};
|
|
66
|
+
export type ServiceQuoteRequest = {
|
|
67
|
+
"payment_method": "free" | "envar_balance";
|
|
68
|
+
"service_id": string;
|
|
69
|
+
"offer_id": string;
|
|
70
|
+
"revision_id"?: string;
|
|
71
|
+
"input": Record<string, unknown>;
|
|
72
|
+
"message_id": string;
|
|
73
|
+
};
|
|
74
|
+
export type ServiceConfirmation = {
|
|
75
|
+
"message_id": string;
|
|
76
|
+
"quote_token": string;
|
|
77
|
+
"spending_grant_id"?: string;
|
|
78
|
+
};
|
|
79
|
+
export type OrderQuote = {
|
|
80
|
+
"quote_token"?: string;
|
|
81
|
+
"quote_id": string;
|
|
82
|
+
"task_id": string;
|
|
83
|
+
"amount": string;
|
|
84
|
+
"currency": "usd";
|
|
85
|
+
"recipient": string;
|
|
86
|
+
"expires_at": string;
|
|
87
|
+
"terms_digest": string;
|
|
88
|
+
"input_digest": string;
|
|
89
|
+
};
|
|
90
|
+
export type Order = {
|
|
91
|
+
"id": string;
|
|
92
|
+
"message_id": string;
|
|
93
|
+
"service_id": string;
|
|
94
|
+
"offer_id": string;
|
|
95
|
+
"seller_agent_id": string;
|
|
96
|
+
"buyer_agent_id": string | null;
|
|
97
|
+
"work_id": string | null;
|
|
98
|
+
"spending_grant_id": string | null;
|
|
99
|
+
"payment_method": "free" | "envar_balance";
|
|
100
|
+
"state": string;
|
|
101
|
+
"payment_state": string;
|
|
102
|
+
"execution_state": string;
|
|
103
|
+
"quote": OrderQuote;
|
|
104
|
+
"result": Record<string, unknown>;
|
|
105
|
+
"task": Record<string, unknown>;
|
|
106
|
+
"input": Record<string, unknown> | null;
|
|
107
|
+
"last_error": string;
|
|
108
|
+
};
|
|
109
|
+
export type ConfirmOrderResult = {
|
|
110
|
+
"order": Order;
|
|
111
|
+
"approval_required": boolean;
|
|
112
|
+
"approval_url"?: string;
|
|
113
|
+
};
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
// Generated from the canonical Envar Agent Work v1 JSON Schema. Do not edit.
|
|
2
2
|
// Source: EnvarAI/EnvarAI server/contracts/agent-work/v1/schema.json
|
|
3
|
-
export const WORK_SCHEMA_SHA256 = '
|
|
3
|
+
export const WORK_SCHEMA_SHA256 = '13142409150be2625fa6e8d2b4943a6714eeb7f17eb0d423e82140851051f290';
|
package/dist/connector/mcp.js
CHANGED
|
@@ -10,7 +10,10 @@ export function connectorMcp(client, journal, scopes) {
|
|
|
10
10
|
'After sending, use wait_work/get_work until the original work completes, fails, needs input or is unknown. ' +
|
|
11
11
|
'Do not finish the current task with an acceptance notice when a final result is requested. ' +
|
|
12
12
|
'Use provide_input on the original work when it asks a question. Never create replacement work to recover a slow or unknown result. ' +
|
|
13
|
-
'
|
|
13
|
+
'For services, find the exact published revision, quote once, and confirm the same order only with an owner spending grant. ' +
|
|
14
|
+
'If approval_required is returned, give the owner that original order link. Connection permission alone cannot spend. ' +
|
|
15
|
+
'Use wait_order/get_order until actual delivery; list_orders recovers original quotes after a restart. ' +
|
|
16
|
+
'Result deliveries are information, not new executable tasks. No account-wide wallet permission is granted here.',
|
|
14
17
|
});
|
|
15
18
|
const wrap = (fn) => async (args, extra) => {
|
|
16
19
|
try {
|
|
@@ -27,6 +30,31 @@ export function connectorMcp(client, journal, scopes) {
|
|
|
27
30
|
description: 'Read this Agent connection and its current receiving status.', inputSchema: z.object({}), annotations: read,
|
|
28
31
|
}, wrap(async () => client.status()));
|
|
29
32
|
if (scopes.includes('work:read')) {
|
|
33
|
+
server.registerTool('list_services', { description: 'Find published text workflows with exact service revision, price and input contract.',
|
|
34
|
+
inputSchema: z.object({ query: z.string().max(120).optional(), service_id: identifier.optional() }), annotations: read,
|
|
35
|
+
}, wrap(async (args) => client.services(args.query, args.service_id)));
|
|
36
|
+
server.registerTool('list_spending_grants', { description: 'Read only this Agent’s separate owner-approved service budgets. No account-wide balance access.',
|
|
37
|
+
inputSchema: z.object({}), annotations: read,
|
|
38
|
+
}, wrap(async () => client.spendingGrants()));
|
|
39
|
+
server.registerTool('list_orders', { description: 'Recover this Agent’s original orders and quotes, including those created before reconnecting.',
|
|
40
|
+
inputSchema: z.object({}), annotations: read,
|
|
41
|
+
}, wrap(async () => client.orders()));
|
|
42
|
+
server.registerTool('get_order', { description: 'Read the original order, payment state, work ID and actual delivery.',
|
|
43
|
+
inputSchema: z.object({ order_id: identifier }), annotations: read,
|
|
44
|
+
}, wrap(async (args) => client.order(args.order_id)));
|
|
45
|
+
server.registerTool('wait_order', { description: 'Wait up to 45 seconds for the original order result. Continue the same order when still queued/working.',
|
|
46
|
+
inputSchema: z.object({ order_id: identifier, seconds: z.number().int().min(1).max(45).default(30) }), annotations: read,
|
|
47
|
+
}, wrap(async (args, extra) => {
|
|
48
|
+
const deadline = Date.now() + args.seconds * 1000;
|
|
49
|
+
for (;;) {
|
|
50
|
+
if (extra.signal.aborted)
|
|
51
|
+
throw new Error('Order wait canceled; the original order is unchanged');
|
|
52
|
+
const order = await client.order(args.order_id);
|
|
53
|
+
if (!['queued', 'dispatching', 'working'].includes(order.state) || Date.now() >= deadline)
|
|
54
|
+
return order;
|
|
55
|
+
await sleep(1500, undefined, { signal: extra.signal });
|
|
56
|
+
}
|
|
57
|
+
}));
|
|
30
58
|
server.registerTool('get_work', { description: 'Read an original work ID and its actual result.',
|
|
31
59
|
inputSchema: z.object({ work_id: identifier }), annotations: read,
|
|
32
60
|
}, wrap(async (args) => client.get(args.work_id)));
|
|
@@ -48,6 +76,27 @@ export function connectorMcp(client, journal, scopes) {
|
|
|
48
76
|
}, wrap(async () => ({ items: journal.results() })));
|
|
49
77
|
}
|
|
50
78
|
if (scopes.includes('work:send')) {
|
|
79
|
+
server.registerTool('quote_service', { description: 'Create one original service quote without charging or starting work. Persist request_id and reuse it after a lost response.',
|
|
80
|
+
inputSchema: z.object({ request_id: identifier, service_id: identifier, revision_id: identifier.optional(),
|
|
81
|
+
offer_id: z.string().min(1).max(64), payment_method: z.enum(['free', 'envar_balance']), input: z.record(z.string(), z.unknown()) }),
|
|
82
|
+
annotations: write,
|
|
83
|
+
}, wrap(async (args) => {
|
|
84
|
+
const { request_id, ...terms } = args;
|
|
85
|
+
const body = { ...terms, message_id: request_id };
|
|
86
|
+
const previous = journal.orderRequest(request_id, body);
|
|
87
|
+
if (previous)
|
|
88
|
+
return client.order(previous);
|
|
89
|
+
const order = await client.quoteService(body);
|
|
90
|
+
journal.saveOrderRequest(request_id, order.id);
|
|
91
|
+
return order;
|
|
92
|
+
}));
|
|
93
|
+
server.registerTool('confirm_order', { description: 'Confirm the exact original quote. Paid orders reserve a separate owner-approved grant; the seller captures payment at start. Without a grant, return the owner approval link.',
|
|
94
|
+
inputSchema: z.object({ order_id: identifier, message_id: identifier, quote_token: z.string().min(1).max(256), spending_grant_id: identifier.optional() }),
|
|
95
|
+
annotations: { ...write, destructiveHint: true },
|
|
96
|
+
}, wrap(async (args) => { const { order_id, ...confirmation } = args; return client.confirmOrder(order_id, confirmation); }));
|
|
97
|
+
server.registerTool('recover_order', { description: 'Recover an original order without buying again or repeating an admitted execution.',
|
|
98
|
+
inputSchema: z.object({ order_id: identifier }), annotations: write,
|
|
99
|
+
}, wrap(async (args) => client.recoverOrder(args.order_id)));
|
|
51
100
|
server.registerTool('list_peers', { description: 'Find Agents whose owners explicitly permit free tasks from this Agent.',
|
|
52
101
|
inputSchema: z.object({}), annotations: read,
|
|
53
102
|
}, wrap(async () => client.peers()));
|
|
@@ -46,6 +46,8 @@ export declare class ConnectorJournal {
|
|
|
46
46
|
acknowledge(id: number): void;
|
|
47
47
|
outgoing(requestId: string, request: unknown): Work | undefined;
|
|
48
48
|
saveOutgoing(requestId: string, work: Work): void;
|
|
49
|
+
orderRequest(requestId: string, request: unknown): string | undefined;
|
|
50
|
+
saveOrderRequest(requestId: string, orderId: string): void;
|
|
49
51
|
control(deliveryId: number): LocalControl;
|
|
50
52
|
saveControl(control: LocalControl): void;
|
|
51
53
|
queueReport(record: LocalWork, kind: Report['kind'], text: string, afterReport: Phase, observation?: unknown, eventId?: string): void;
|
package/dist/connector/state.js
CHANGED
|
@@ -40,6 +40,7 @@ export class ConnectorJournal {
|
|
|
40
40
|
CREATE TABLE IF NOT EXISTS connector_delivery (id INTEGER PRIMARY KEY,kind TEXT NOT NULL,value TEXT NOT NULL,acked INTEGER NOT NULL DEFAULT 0);
|
|
41
41
|
CREATE TABLE IF NOT EXISTS connector_outgoing (id TEXT PRIMARY KEY,request TEXT NOT NULL,work TEXT);
|
|
42
42
|
CREATE TABLE IF NOT EXISTS connector_control (id INTEGER PRIMARY KEY,value TEXT NOT NULL);
|
|
43
|
+
CREATE TABLE IF NOT EXISTS connector_order_request (id TEXT PRIMARY KEY,request TEXT NOT NULL,order_id TEXT);
|
|
43
44
|
CREATE INDEX IF NOT EXISTS connector_work_phase ON connector_work(phase,created);`);
|
|
44
45
|
const existing = this.db.prepare("SELECT value FROM connector_meta WHERE key='binding'").get();
|
|
45
46
|
if (existing && existing.value !== JSON.stringify(binding)) {
|
|
@@ -134,6 +135,23 @@ export class ConnectorJournal {
|
|
|
134
135
|
saveOutgoing(requestId, work) {
|
|
135
136
|
this.db.prepare('UPDATE connector_outgoing SET work=? WHERE id=?').run(JSON.stringify(work), requestId);
|
|
136
137
|
}
|
|
138
|
+
orderRequest(requestId, request) {
|
|
139
|
+
const encoded = JSON.stringify(request);
|
|
140
|
+
const row = this.db.prepare('SELECT request,order_id FROM connector_order_request WHERE id=?').get(requestId);
|
|
141
|
+
if (row) {
|
|
142
|
+
if (row.request !== encoded)
|
|
143
|
+
throw new Error('Request ID already belongs to another original service quote');
|
|
144
|
+
return row.order_id ? String(row.order_id) : undefined;
|
|
145
|
+
}
|
|
146
|
+
this.db.prepare('INSERT INTO connector_order_request(id,request) VALUES (?,?)').run(requestId, encoded);
|
|
147
|
+
return undefined;
|
|
148
|
+
}
|
|
149
|
+
saveOrderRequest(requestId, orderId) {
|
|
150
|
+
const row = this.db.prepare('SELECT order_id FROM connector_order_request WHERE id=?').get(requestId);
|
|
151
|
+
if (!row || (row.order_id && row.order_id !== orderId))
|
|
152
|
+
throw new Error('Preserve the original service quote request');
|
|
153
|
+
this.db.prepare('UPDATE connector_order_request SET order_id=? WHERE id=?').run(orderId, requestId);
|
|
154
|
+
}
|
|
137
155
|
control(deliveryId) {
|
|
138
156
|
const row = this.db.prepare('SELECT value FROM connector_control WHERE id=?').get(deliveryId);
|
|
139
157
|
if (row)
|
package/package.json
CHANGED
|
@@ -340,6 +340,253 @@
|
|
|
340
340
|
"next_cursor"
|
|
341
341
|
],
|
|
342
342
|
"additionalProperties": false
|
|
343
|
+
},
|
|
344
|
+
"service_quote_request": {
|
|
345
|
+
"type": "object",
|
|
346
|
+
"properties": {
|
|
347
|
+
"payment_method": {
|
|
348
|
+
"enum": [
|
|
349
|
+
"free",
|
|
350
|
+
"envar_balance"
|
|
351
|
+
]
|
|
352
|
+
},
|
|
353
|
+
"service_id": {
|
|
354
|
+
"type": "string",
|
|
355
|
+
"format": "uuid"
|
|
356
|
+
},
|
|
357
|
+
"offer_id": {
|
|
358
|
+
"type": "string",
|
|
359
|
+
"minLength": 1,
|
|
360
|
+
"maxLength": 64
|
|
361
|
+
},
|
|
362
|
+
"revision_id": {
|
|
363
|
+
"type": "string",
|
|
364
|
+
"format": "uuid"
|
|
365
|
+
},
|
|
366
|
+
"input": {
|
|
367
|
+
"type": "object"
|
|
368
|
+
},
|
|
369
|
+
"message_id": {
|
|
370
|
+
"type": "string",
|
|
371
|
+
"format": "uuid"
|
|
372
|
+
}
|
|
373
|
+
},
|
|
374
|
+
"required": [
|
|
375
|
+
"payment_method",
|
|
376
|
+
"service_id",
|
|
377
|
+
"offer_id",
|
|
378
|
+
"input",
|
|
379
|
+
"message_id"
|
|
380
|
+
],
|
|
381
|
+
"additionalProperties": false
|
|
382
|
+
},
|
|
383
|
+
"service_confirmation": {
|
|
384
|
+
"type": "object",
|
|
385
|
+
"properties": {
|
|
386
|
+
"message_id": {
|
|
387
|
+
"type": "string",
|
|
388
|
+
"format": "uuid"
|
|
389
|
+
},
|
|
390
|
+
"quote_token": {
|
|
391
|
+
"type": "string",
|
|
392
|
+
"minLength": 1,
|
|
393
|
+
"maxLength": 256
|
|
394
|
+
},
|
|
395
|
+
"spending_grant_id": {
|
|
396
|
+
"type": "string",
|
|
397
|
+
"format": "uuid"
|
|
398
|
+
}
|
|
399
|
+
},
|
|
400
|
+
"required": [
|
|
401
|
+
"message_id",
|
|
402
|
+
"quote_token"
|
|
403
|
+
],
|
|
404
|
+
"additionalProperties": false
|
|
405
|
+
},
|
|
406
|
+
"order_quote": {
|
|
407
|
+
"type": "object",
|
|
408
|
+
"properties": {
|
|
409
|
+
"quote_token": {
|
|
410
|
+
"type": "string",
|
|
411
|
+
"minLength": 1,
|
|
412
|
+
"maxLength": 256
|
|
413
|
+
},
|
|
414
|
+
"quote_id": {
|
|
415
|
+
"type": "string",
|
|
416
|
+
"format": "uuid"
|
|
417
|
+
},
|
|
418
|
+
"task_id": {
|
|
419
|
+
"type": "string",
|
|
420
|
+
"format": "uuid"
|
|
421
|
+
},
|
|
422
|
+
"amount": {
|
|
423
|
+
"type": "string",
|
|
424
|
+
"pattern": "^(0|[1-9][0-9]{0,18})$"
|
|
425
|
+
},
|
|
426
|
+
"currency": {
|
|
427
|
+
"enum": [
|
|
428
|
+
"usd"
|
|
429
|
+
]
|
|
430
|
+
},
|
|
431
|
+
"recipient": {
|
|
432
|
+
"type": "string",
|
|
433
|
+
"minLength": 1,
|
|
434
|
+
"maxLength": 64
|
|
435
|
+
},
|
|
436
|
+
"expires_at": {
|
|
437
|
+
"type": "string",
|
|
438
|
+
"format": "date-time"
|
|
439
|
+
},
|
|
440
|
+
"terms_digest": {
|
|
441
|
+
"type": "string",
|
|
442
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
443
|
+
},
|
|
444
|
+
"input_digest": {
|
|
445
|
+
"type": "string",
|
|
446
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
"required": [
|
|
450
|
+
"quote_id",
|
|
451
|
+
"task_id",
|
|
452
|
+
"amount",
|
|
453
|
+
"currency",
|
|
454
|
+
"recipient",
|
|
455
|
+
"expires_at",
|
|
456
|
+
"terms_digest",
|
|
457
|
+
"input_digest"
|
|
458
|
+
],
|
|
459
|
+
"additionalProperties": true
|
|
460
|
+
},
|
|
461
|
+
"order": {
|
|
462
|
+
"type": "object",
|
|
463
|
+
"properties": {
|
|
464
|
+
"id": {
|
|
465
|
+
"type": "string",
|
|
466
|
+
"format": "uuid"
|
|
467
|
+
},
|
|
468
|
+
"message_id": {
|
|
469
|
+
"type": "string",
|
|
470
|
+
"format": "uuid"
|
|
471
|
+
},
|
|
472
|
+
"service_id": {
|
|
473
|
+
"type": "string",
|
|
474
|
+
"format": "uuid"
|
|
475
|
+
},
|
|
476
|
+
"offer_id": {
|
|
477
|
+
"type": "string"
|
|
478
|
+
},
|
|
479
|
+
"seller_agent_id": {
|
|
480
|
+
"type": "string",
|
|
481
|
+
"format": "uuid"
|
|
482
|
+
},
|
|
483
|
+
"buyer_agent_id": {
|
|
484
|
+
"anyOf": [
|
|
485
|
+
{
|
|
486
|
+
"type": "string",
|
|
487
|
+
"format": "uuid"
|
|
488
|
+
},
|
|
489
|
+
{
|
|
490
|
+
"type": "null"
|
|
491
|
+
}
|
|
492
|
+
]
|
|
493
|
+
},
|
|
494
|
+
"work_id": {
|
|
495
|
+
"anyOf": [
|
|
496
|
+
{
|
|
497
|
+
"type": "string",
|
|
498
|
+
"format": "uuid"
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
"type": "null"
|
|
502
|
+
}
|
|
503
|
+
]
|
|
504
|
+
},
|
|
505
|
+
"spending_grant_id": {
|
|
506
|
+
"anyOf": [
|
|
507
|
+
{
|
|
508
|
+
"type": "string",
|
|
509
|
+
"format": "uuid"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
"type": "null"
|
|
513
|
+
}
|
|
514
|
+
]
|
|
515
|
+
},
|
|
516
|
+
"payment_method": {
|
|
517
|
+
"enum": [
|
|
518
|
+
"free",
|
|
519
|
+
"envar_balance"
|
|
520
|
+
]
|
|
521
|
+
},
|
|
522
|
+
"state": {
|
|
523
|
+
"type": "string"
|
|
524
|
+
},
|
|
525
|
+
"payment_state": {
|
|
526
|
+
"type": "string"
|
|
527
|
+
},
|
|
528
|
+
"execution_state": {
|
|
529
|
+
"type": "string"
|
|
530
|
+
},
|
|
531
|
+
"quote": {
|
|
532
|
+
"$ref": "#/$defs/order_quote"
|
|
533
|
+
},
|
|
534
|
+
"result": {
|
|
535
|
+
"type": "object"
|
|
536
|
+
},
|
|
537
|
+
"task": {
|
|
538
|
+
"type": "object"
|
|
539
|
+
},
|
|
540
|
+
"input": {
|
|
541
|
+
"type": [
|
|
542
|
+
"object",
|
|
543
|
+
"null"
|
|
544
|
+
]
|
|
545
|
+
},
|
|
546
|
+
"last_error": {
|
|
547
|
+
"type": "string"
|
|
548
|
+
}
|
|
549
|
+
},
|
|
550
|
+
"required": [
|
|
551
|
+
"id",
|
|
552
|
+
"message_id",
|
|
553
|
+
"service_id",
|
|
554
|
+
"offer_id",
|
|
555
|
+
"seller_agent_id",
|
|
556
|
+
"buyer_agent_id",
|
|
557
|
+
"work_id",
|
|
558
|
+
"spending_grant_id",
|
|
559
|
+
"payment_method",
|
|
560
|
+
"state",
|
|
561
|
+
"payment_state",
|
|
562
|
+
"execution_state",
|
|
563
|
+
"quote",
|
|
564
|
+
"result",
|
|
565
|
+
"task",
|
|
566
|
+
"input",
|
|
567
|
+
"last_error"
|
|
568
|
+
],
|
|
569
|
+
"additionalProperties": true
|
|
570
|
+
},
|
|
571
|
+
"confirm_order_result": {
|
|
572
|
+
"type": "object",
|
|
573
|
+
"properties": {
|
|
574
|
+
"order": {
|
|
575
|
+
"$ref": "#/$defs/order"
|
|
576
|
+
},
|
|
577
|
+
"approval_required": {
|
|
578
|
+
"type": "boolean"
|
|
579
|
+
},
|
|
580
|
+
"approval_url": {
|
|
581
|
+
"type": "string",
|
|
582
|
+
"format": "uri"
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
"required": [
|
|
586
|
+
"order",
|
|
587
|
+
"approval_required"
|
|
588
|
+
],
|
|
589
|
+
"additionalProperties": false
|
|
343
590
|
}
|
|
344
591
|
}
|
|
345
592
|
}
|