@evomap/evolver-adapter-public 2.0.0-beta.1 → 2.0.0-beta.4
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/dist/agentDirectory.d.ts +12 -0
- package/dist/agentDirectory.js +293 -0
- package/dist/antiAbuseTelemetry.d.ts +1 -0
- package/dist/antiAbuseTelemetry.js +9 -2
- package/dist/auth/workspaceKeychain.d.ts +37 -0
- package/dist/auth/workspaceKeychain.js +251 -0
- package/dist/connect.js +15 -1
- package/dist/hubCapability.d.ts +30 -1
- package/dist/hubCapability.js +157 -12
- package/dist/hubReuse.d.ts +1 -2
- package/dist/hubReuse.js +4 -1
- package/dist/hubUrl.d.ts +4 -0
- package/dist/hubUrl.js +20 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/package.json +10 -2
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type hub } from '@evomap/evolver-core';
|
|
2
|
+
export declare const PUBLIC_TASK_DISCOVERY_MAX_CANDIDATES: number;
|
|
3
|
+
export declare function publicAgentSearchQuery(request: hub.AgentSearchRequest): Record<string, string | number | undefined>;
|
|
4
|
+
export declare function unsupportedPublicAvailability(availability: hub.AgentAvailability | undefined): hub.AgentDirectoryResult<never> | undefined;
|
|
5
|
+
export declare function unsupportedPublicSort(sort: hub.AgentDirectorySort | undefined): hub.AgentDirectoryResult<never> | undefined;
|
|
6
|
+
export declare function publicTaskDiscoveryQuery(request: hub.AgentTaskDiscoveryRequest): Record<string, string | number | undefined>;
|
|
7
|
+
export declare function parsePublicAgentPage(body: unknown): hub.AgentDirectoryResult<hub.AgentDirectoryPage>;
|
|
8
|
+
export declare function paginatePublicAgentPage(page: hub.AgentDirectoryResult<hub.AgentDirectoryPage>, request: hub.AgentSearchRequest, maxOffset?: number): hub.AgentDirectoryResult<hub.AgentDirectoryPage>;
|
|
9
|
+
export declare function mergePublicAgentPages(pages: readonly hub.AgentDirectoryResult<hub.AgentDirectoryPage>[]): hub.AgentDirectoryResult<hub.AgentDirectoryPage>;
|
|
10
|
+
export declare function parsePublicAgentProfile(body: unknown): hub.AgentDirectoryResult<hub.AgentProfile | null>;
|
|
11
|
+
export declare function agentDirectoryFailure(error: unknown): hub.AgentDirectoryResult<never>;
|
|
12
|
+
export declare function withDirectoryTimeout<T>(operation: Promise<T>, timeoutMs: number): Promise<T>;
|
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
import { hub as hubNs } from '@evomap/evolver-core';
|
|
2
|
+
import { AuthError, HubClientError, HubUnreachableError } from './hubFetch.js';
|
|
3
|
+
const MAX_TEXT_LENGTH = 500;
|
|
4
|
+
const MAX_CAPABILITY_COUNT = 50;
|
|
5
|
+
export const PUBLIC_TASK_DISCOVERY_MAX_CANDIDATES = hubNs.AGENT_DIRECTORY_MAX_LIMIT * 2;
|
|
6
|
+
export function publicAgentSearchQuery(request) {
|
|
7
|
+
const normalized = hubNs.normalizeAgentSearchRequest(request);
|
|
8
|
+
return {
|
|
9
|
+
...(normalized.query ? { q: normalized.query } : {}),
|
|
10
|
+
...(normalized.signals && normalized.signals.length > 0 ? { signals: normalized.signals.join(',') } : {}),
|
|
11
|
+
limit: hubNs.AGENT_DIRECTORY_MAX_LIMIT,
|
|
12
|
+
online_only: normalized.availability === 'offline' ? 'false' : 'true',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function unsupportedPublicAvailability(availability) {
|
|
16
|
+
if (availability === 'busy' || availability === 'offline' || availability === 'unknown') {
|
|
17
|
+
return hubNs.capabilityUnavailable(`public_hub_availability_${availability}_not_supported`);
|
|
18
|
+
}
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
export function unsupportedPublicSort(sort) {
|
|
22
|
+
if (sort === 'recent')
|
|
23
|
+
return hubNs.capabilityUnavailable('public_hub_sort_recent_not_supported');
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
export function publicTaskDiscoveryQuery(request) {
|
|
27
|
+
return publicAgentSearchQuery(hubNs.normalizeAgentTaskDiscoveryRequest(request));
|
|
28
|
+
}
|
|
29
|
+
export function parsePublicAgentPage(body) {
|
|
30
|
+
const record = asRecord(body);
|
|
31
|
+
const payload = asRecord(record?.['payload']) ?? record;
|
|
32
|
+
const rawItems = Array.isArray(payload?.['results'])
|
|
33
|
+
? payload['results']
|
|
34
|
+
: Array.isArray(payload?.['agents'])
|
|
35
|
+
? payload['agents']
|
|
36
|
+
: Array.isArray(body)
|
|
37
|
+
? body
|
|
38
|
+
: undefined;
|
|
39
|
+
if (!rawItems)
|
|
40
|
+
return invalidResponse('agent_directory_results_missing');
|
|
41
|
+
const items = [];
|
|
42
|
+
for (const raw of rawItems) {
|
|
43
|
+
const parsed = parseAgentEntry(raw);
|
|
44
|
+
if (!parsed)
|
|
45
|
+
return invalidResponse('agent_directory_entry_invalid');
|
|
46
|
+
items.push(parsed);
|
|
47
|
+
}
|
|
48
|
+
const nextCursor = optionalString(payload?.['next_cursor'] ?? payload?.['nextCursor'], 256);
|
|
49
|
+
const hasMoreValue = payload?.['has_more'] ?? payload?.['hasMore'];
|
|
50
|
+
if (hasMoreValue !== undefined && typeof hasMoreValue !== 'boolean')
|
|
51
|
+
return invalidResponse('agent_directory_has_more_invalid');
|
|
52
|
+
return { ok: true, value: { items, ...(nextCursor ? { nextCursor } : {}), hasMore: typeof hasMoreValue === 'boolean' ? hasMoreValue : Boolean(nextCursor) } };
|
|
53
|
+
}
|
|
54
|
+
export function paginatePublicAgentPage(page, request, maxOffset = hubNs.AGENT_DIRECTORY_MAX_LIMIT) {
|
|
55
|
+
if (!page.ok)
|
|
56
|
+
return page;
|
|
57
|
+
const normalized = hubNs.normalizeAgentSearchRequest(request);
|
|
58
|
+
const offset = cursorOffset(normalized.cursor, maxOffset);
|
|
59
|
+
const filtered = normalized.availability
|
|
60
|
+
? page.value.items.filter((item) => item.availability === normalized.availability)
|
|
61
|
+
: page.value.items;
|
|
62
|
+
const items = [...filtered].sort(agentComparator(normalized.sort, normalized.order));
|
|
63
|
+
const slice = items.slice(offset, offset + normalized.limit);
|
|
64
|
+
const nextOffset = offset + slice.length;
|
|
65
|
+
return {
|
|
66
|
+
ok: true,
|
|
67
|
+
value: {
|
|
68
|
+
items: slice,
|
|
69
|
+
...(nextOffset < items.length ? { nextCursor: `offset:${nextOffset}` } : {}),
|
|
70
|
+
hasMore: nextOffset < items.length,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
export function mergePublicAgentPages(pages) {
|
|
75
|
+
const failed = pages.find((page) => !page.ok);
|
|
76
|
+
if (failed && !failed.ok)
|
|
77
|
+
return failed;
|
|
78
|
+
const items = new Map();
|
|
79
|
+
for (const page of pages) {
|
|
80
|
+
if (!page.ok)
|
|
81
|
+
continue;
|
|
82
|
+
for (const item of page.value.items) {
|
|
83
|
+
const previous = items.get(item.agentId);
|
|
84
|
+
if (!previous || (item.score ?? -1) > (previous.score ?? -1))
|
|
85
|
+
items.set(item.agentId, item);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { ok: true, value: { items: [...items.values()], hasMore: false } };
|
|
89
|
+
}
|
|
90
|
+
export function parsePublicAgentProfile(body) {
|
|
91
|
+
if (body === null)
|
|
92
|
+
return { ok: true, value: null };
|
|
93
|
+
const record = asRecord(body);
|
|
94
|
+
const payload = asRecord(record?.['payload']) ?? record;
|
|
95
|
+
const profile = asRecord(payload?.['profile']) ?? payload;
|
|
96
|
+
if (!profile)
|
|
97
|
+
return invalidResponse('agent_profile_invalid');
|
|
98
|
+
const entry = parseAgentEntry(profile);
|
|
99
|
+
if (!entry)
|
|
100
|
+
return invalidResponse('agent_profile_invalid');
|
|
101
|
+
const taskStatsCompletedCount = completedTaskCountFromTaskStats(profile['task_stats']);
|
|
102
|
+
if (taskStatsCompletedCount === null)
|
|
103
|
+
return invalidResponse('agent_profile_invalid');
|
|
104
|
+
const { score: _score, ...safeProfile } = entry;
|
|
105
|
+
return {
|
|
106
|
+
ok: true,
|
|
107
|
+
value: {
|
|
108
|
+
...safeProfile,
|
|
109
|
+
...(safeProfile.completedTaskCount === undefined && taskStatsCompletedCount !== undefined
|
|
110
|
+
? { completedTaskCount: taskStatsCompletedCount }
|
|
111
|
+
: {}),
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export function agentDirectoryFailure(error) {
|
|
116
|
+
if (error instanceof hubNs.AgentDirectoryInputError)
|
|
117
|
+
return { ok: false, error: { code: 'invalid_request', retryable: false, message: error.message } };
|
|
118
|
+
if (error instanceof DirectoryTimeoutError)
|
|
119
|
+
return { ok: false, error: { code: 'timeout', retryable: true, message: 'agent_directory_timeout' } };
|
|
120
|
+
if (error instanceof AuthError)
|
|
121
|
+
return { ok: false, error: { code: 'permission_denied', retryable: false, message: 'agent_directory_permission_denied' } };
|
|
122
|
+
if (error instanceof HubClientError && (error.status === 404 || error.status === 405 || error.status === 501))
|
|
123
|
+
return hubNs.capabilityUnavailable();
|
|
124
|
+
if (error instanceof HubUnreachableError || isNetworkError(error))
|
|
125
|
+
return { ok: false, error: { code: 'hub_unavailable', retryable: true, message: 'agent_directory_hub_unavailable' } };
|
|
126
|
+
if (error instanceof HubClientError)
|
|
127
|
+
return { ok: false, error: { code: 'invalid_request', retryable: false, message: `agent_directory_hub_${error.status}` } };
|
|
128
|
+
return { ok: false, error: { code: 'hub_unavailable', retryable: true, message: 'agent_directory_request_failed' } };
|
|
129
|
+
}
|
|
130
|
+
export async function withDirectoryTimeout(operation, timeoutMs) {
|
|
131
|
+
let timer;
|
|
132
|
+
try {
|
|
133
|
+
return await Promise.race([
|
|
134
|
+
operation,
|
|
135
|
+
new Promise((_resolve, reject) => {
|
|
136
|
+
timer = setTimeout(() => reject(new DirectoryTimeoutError()), timeoutMs);
|
|
137
|
+
}),
|
|
138
|
+
]);
|
|
139
|
+
}
|
|
140
|
+
finally {
|
|
141
|
+
if (timer)
|
|
142
|
+
clearTimeout(timer);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
class DirectoryTimeoutError extends Error {
|
|
146
|
+
}
|
|
147
|
+
function parseAgentEntry(value) {
|
|
148
|
+
const record = asRecord(value);
|
|
149
|
+
if (!record)
|
|
150
|
+
return undefined;
|
|
151
|
+
const agentId = optionalString(record['agent_id'] ?? record['agentId'] ?? record['node_id'] ?? record['nodeId'], 128);
|
|
152
|
+
if (!agentId)
|
|
153
|
+
return undefined;
|
|
154
|
+
const capabilitiesValue = record['capabilities'];
|
|
155
|
+
const capabilities = stringList(capabilitiesValue === undefined || capabilitiesValue === null ? record['signals'] : capabilitiesValue);
|
|
156
|
+
const domains = stringArray(record['domains']);
|
|
157
|
+
if (capabilities === null || domains === null)
|
|
158
|
+
return undefined;
|
|
159
|
+
const availability = availabilityValue(record['availability'], record['online']);
|
|
160
|
+
const score = optionalBoundedNumber(record['score'], 0, 1);
|
|
161
|
+
const reputation = optionalBoundedNumber(record['reputation'], 0, 1_000_000);
|
|
162
|
+
const completedTaskCount = optionalInteger(record['completed_tasks'] ?? record['completedTaskCount'], 0);
|
|
163
|
+
const lastSeenAt = optionalTimestamp(record['last_seen_at'] ?? record['lastSeenAt']);
|
|
164
|
+
if (availability === null || score === null || reputation === null || completedTaskCount === null || lastSeenAt === null)
|
|
165
|
+
return undefined;
|
|
166
|
+
return {
|
|
167
|
+
agentId,
|
|
168
|
+
...(optionalString(record['display_name'] ?? record['displayName'] ?? record['name'] ?? record['alias'], 120) ? { displayName: optionalString(record['display_name'] ?? record['displayName'] ?? record['name'] ?? record['alias'], 120) } : {}),
|
|
169
|
+
...(optionalString(record['summary'] ?? record['description'], MAX_TEXT_LENGTH) ? { summary: optionalString(record['summary'] ?? record['description'], MAX_TEXT_LENGTH) } : {}),
|
|
170
|
+
...(capabilities !== undefined ? { capabilities } : {}),
|
|
171
|
+
...(domains !== undefined ? { domains } : {}),
|
|
172
|
+
...(score !== undefined ? { score } : {}),
|
|
173
|
+
...(reputation !== undefined ? { reputation } : {}),
|
|
174
|
+
...(completedTaskCount !== undefined ? { completedTaskCount } : {}),
|
|
175
|
+
...(availability ? { availability } : {}),
|
|
176
|
+
...(lastSeenAt !== undefined ? { lastSeenAt } : {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
function asRecord(value) {
|
|
180
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
181
|
+
}
|
|
182
|
+
function hasOwn(record, key) {
|
|
183
|
+
return Object.prototype.hasOwnProperty.call(record, key);
|
|
184
|
+
}
|
|
185
|
+
function optionalString(value, maxLength) {
|
|
186
|
+
if (value === undefined || value === null)
|
|
187
|
+
return undefined;
|
|
188
|
+
if (typeof value !== 'string')
|
|
189
|
+
return undefined;
|
|
190
|
+
const text = value.trim();
|
|
191
|
+
return text && text.length <= maxLength ? text : undefined;
|
|
192
|
+
}
|
|
193
|
+
function stringArray(value) {
|
|
194
|
+
if (value === undefined || value === null)
|
|
195
|
+
return undefined;
|
|
196
|
+
if (!Array.isArray(value) || value.length > MAX_CAPABILITY_COUNT)
|
|
197
|
+
return null;
|
|
198
|
+
const output = [];
|
|
199
|
+
for (const item of value) {
|
|
200
|
+
const text = optionalString(item, 64);
|
|
201
|
+
if (!text)
|
|
202
|
+
return null;
|
|
203
|
+
if (!output.includes(text))
|
|
204
|
+
output.push(text);
|
|
205
|
+
}
|
|
206
|
+
return output;
|
|
207
|
+
}
|
|
208
|
+
function stringList(value) {
|
|
209
|
+
if (typeof value === 'string')
|
|
210
|
+
return stringArray(value.split(',').map((item) => item.trim()).filter(Boolean));
|
|
211
|
+
return stringArray(value);
|
|
212
|
+
}
|
|
213
|
+
function completedTaskCountFromTaskStats(value) {
|
|
214
|
+
if (value === undefined)
|
|
215
|
+
return undefined;
|
|
216
|
+
const taskStats = asRecord(value);
|
|
217
|
+
if (!taskStats)
|
|
218
|
+
return null;
|
|
219
|
+
if (!hasOwn(taskStats, 'completed'))
|
|
220
|
+
return undefined;
|
|
221
|
+
const completed = taskStats['completed'];
|
|
222
|
+
if (!Number.isInteger(completed) || Number(completed) < 0)
|
|
223
|
+
return null;
|
|
224
|
+
return Number(completed);
|
|
225
|
+
}
|
|
226
|
+
function availabilityValue(value, online) {
|
|
227
|
+
if (value === 'online' || value === 'busy' || value === 'offline' || value === 'unknown')
|
|
228
|
+
return value;
|
|
229
|
+
if (value !== undefined)
|
|
230
|
+
return null;
|
|
231
|
+
if (online === true)
|
|
232
|
+
return 'online';
|
|
233
|
+
if (online === false)
|
|
234
|
+
return 'offline';
|
|
235
|
+
if (online !== undefined)
|
|
236
|
+
return null;
|
|
237
|
+
return undefined;
|
|
238
|
+
}
|
|
239
|
+
function optionalBoundedNumber(value, min, max) {
|
|
240
|
+
if (value === undefined || value === null)
|
|
241
|
+
return undefined;
|
|
242
|
+
if (typeof value !== 'number' || !Number.isFinite(value) || value < min || value > max)
|
|
243
|
+
return null;
|
|
244
|
+
return value;
|
|
245
|
+
}
|
|
246
|
+
function optionalInteger(value, min) {
|
|
247
|
+
if (value === undefined || value === null)
|
|
248
|
+
return undefined;
|
|
249
|
+
if (!Number.isInteger(value) || Number(value) < min)
|
|
250
|
+
return null;
|
|
251
|
+
return Number(value);
|
|
252
|
+
}
|
|
253
|
+
function optionalTimestamp(value) {
|
|
254
|
+
if (value === undefined || value === null)
|
|
255
|
+
return undefined;
|
|
256
|
+
if (typeof value === 'number' && Number.isFinite(value) && value >= 0)
|
|
257
|
+
return value;
|
|
258
|
+
if (typeof value === 'string' && value.length <= 64) {
|
|
259
|
+
const parsed = Date.parse(value);
|
|
260
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
261
|
+
}
|
|
262
|
+
return null;
|
|
263
|
+
}
|
|
264
|
+
function invalidResponse(message) {
|
|
265
|
+
return { ok: false, error: { code: 'invalid_response', retryable: false, message } };
|
|
266
|
+
}
|
|
267
|
+
function cursorOffset(cursor, maxOffset) {
|
|
268
|
+
if (!cursor)
|
|
269
|
+
return 0;
|
|
270
|
+
const match = /^offset:(\d+)$/.exec(cursor);
|
|
271
|
+
const offset = match ? Number(match[1]) : NaN;
|
|
272
|
+
if (!Number.isInteger(offset) || offset < 0 || offset > maxOffset) {
|
|
273
|
+
throw new hubNs.AgentDirectoryInputError('cursor_invalid');
|
|
274
|
+
}
|
|
275
|
+
return offset;
|
|
276
|
+
}
|
|
277
|
+
function agentComparator(sort, order) {
|
|
278
|
+
const direction = order === 'asc' ? 1 : -1;
|
|
279
|
+
return (left, right) => direction * (sortValue(left, sort) - sortValue(right, sort)) || left.agentId.localeCompare(right.agentId);
|
|
280
|
+
}
|
|
281
|
+
function sortValue(entry, sort) {
|
|
282
|
+
if (sort === 'reputation')
|
|
283
|
+
return entry.reputation ?? -1;
|
|
284
|
+
if (sort === 'recent')
|
|
285
|
+
return entry.lastSeenAt ?? -1;
|
|
286
|
+
if (sort === 'availability')
|
|
287
|
+
return entry.availability === 'online' ? 3 : entry.availability === 'busy' ? 2 : entry.availability === 'unknown' ? 1 : 0;
|
|
288
|
+
return entry.score ?? -1;
|
|
289
|
+
}
|
|
290
|
+
function isNetworkError(error) {
|
|
291
|
+
const record = asRecord(error);
|
|
292
|
+
return record?.['name'] === 'AbortError' || record?.['name'] === 'TimeoutError' || record?.['code'] === 'HUB_UNREACHABLE';
|
|
293
|
+
}
|
|
@@ -17,6 +17,7 @@ export interface AntiAbuseTelemetryOptions {
|
|
|
17
17
|
evolverVersion?: string;
|
|
18
18
|
taskMeta?: Record<string, unknown>;
|
|
19
19
|
workspaceId?: string;
|
|
20
|
+
workspaceIdResolver?: () => string | null;
|
|
20
21
|
}
|
|
21
22
|
export interface IntegrityHashes {
|
|
22
23
|
package_json_hash: string | null;
|
|
@@ -103,10 +103,10 @@ export function buildHeartbeatAntiAbuseTelemetry(opts = {}) {
|
|
|
103
103
|
const saltId = opts.saltId ?? env['EVOLVER_ANTI_ABUSE_SALT_ID'] ?? (salt ? 'env' : null);
|
|
104
104
|
const pseudonymStatus = salt ? 'salt_configured' : 'salt_missing';
|
|
105
105
|
const devicePseudonym = hmacPseudonym(fp.device, { salt, purpose: 'device' });
|
|
106
|
-
const workspacePseudonym = hmacPseudonym(opts
|
|
106
|
+
const workspacePseudonym = salt ? hmacPseudonym(resolveTelemetryWorkspaceId(opts), { salt, purpose: 'workspace' }) : null;
|
|
107
107
|
const unavailableFields = [
|
|
108
108
|
...(devicePseudonym ? [] : [unavailable('device_pseudonym', 'anti_abuse_salt_missing', 'signed_policy_or_env')]),
|
|
109
|
-
...(workspacePseudonym ? [] : [unavailable('workspace_pseudonym', 'anti_abuse_salt_missing', 'signed_policy_or_env')]),
|
|
109
|
+
...(workspacePseudonym ? [] : [unavailable('workspace_pseudonym', salt ? 'workspace_id_unavailable' : 'anti_abuse_salt_missing', salt ? 'workspace_keychain_or_filesystem' : 'signed_policy_or_env')]),
|
|
110
110
|
unavailable('client_ip', 'server_observed_required', 'hub_edge'),
|
|
111
111
|
unavailable('asn', 'server_observed_required', 'hub_edge'),
|
|
112
112
|
unavailable('proxy_vpn_tor_datacenter_class', 'server_observed_required', 'hub_edge'),
|
|
@@ -213,6 +213,13 @@ function nowIso(now) {
|
|
|
213
213
|
return now;
|
|
214
214
|
return new Date().toISOString();
|
|
215
215
|
}
|
|
216
|
+
function resolveTelemetryWorkspaceId(opts) {
|
|
217
|
+
if (opts.workspaceId)
|
|
218
|
+
return opts.workspaceId;
|
|
219
|
+
if (opts.workspaceIdResolver)
|
|
220
|
+
return opts.workspaceIdResolver();
|
|
221
|
+
return process.cwd();
|
|
222
|
+
}
|
|
216
223
|
function boolFromEnv(value) {
|
|
217
224
|
const v = String(value ?? '').trim().toLowerCase();
|
|
218
225
|
return v === '1' || v === 'true' || v === 'yes' || v === 'on';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export declare const WORKSPACE_KEYCHAIN_SERVICE = "evomap.evolver.workspace-id";
|
|
2
|
+
export type WorkspaceKeychainMode = 'auto' | 'force' | 'off';
|
|
3
|
+
export interface WorkspaceKeychainEntry {
|
|
4
|
+
getPassword(): string | null | undefined;
|
|
5
|
+
setPassword(password: string): void;
|
|
6
|
+
}
|
|
7
|
+
export interface WorkspaceKeychainAddon {
|
|
8
|
+
Entry: new (service: string, account: string) => WorkspaceKeychainEntry;
|
|
9
|
+
}
|
|
10
|
+
export interface WorkspaceKeychainReadResult {
|
|
11
|
+
available: boolean;
|
|
12
|
+
id: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface WorkspaceKeychainOps {
|
|
15
|
+
loadAddon(): WorkspaceKeychainAddon | null;
|
|
16
|
+
readFromKeychain(account: string): WorkspaceKeychainReadResult;
|
|
17
|
+
writeToKeychain(account: string, id: string): boolean;
|
|
18
|
+
getMode(env?: Record<string, string | undefined>): WorkspaceKeychainMode;
|
|
19
|
+
}
|
|
20
|
+
export interface WorkspaceIdentityOptions {
|
|
21
|
+
env?: Record<string, string | undefined>;
|
|
22
|
+
cwd?: string;
|
|
23
|
+
workspaceRoot?: string;
|
|
24
|
+
keychain?: WorkspaceKeychainOps;
|
|
25
|
+
}
|
|
26
|
+
export declare function resetWorkspaceKeychainAddonCacheForTests(): void;
|
|
27
|
+
export declare function getWorkspaceKeychainMode(env?: Record<string, string | undefined>): WorkspaceKeychainMode;
|
|
28
|
+
export declare function loadWorkspaceKeychainAddon(): WorkspaceKeychainAddon | null;
|
|
29
|
+
export declare function isWorkspaceKeychainNoEntryError(err: unknown): boolean;
|
|
30
|
+
export declare function readFromWorkspaceKeychain(account: string, addon?: WorkspaceKeychainAddon | null): WorkspaceKeychainReadResult;
|
|
31
|
+
export declare function writeToWorkspaceKeychain(account: string, id: string, addon?: WorkspaceKeychainAddon | null): boolean;
|
|
32
|
+
export declare const defaultWorkspaceKeychain: WorkspaceKeychainOps;
|
|
33
|
+
export declare function workspaceIdPath(workspaceRoot: string): string;
|
|
34
|
+
export declare function resolveWorkspaceRootForIdentity(opts?: Pick<WorkspaceIdentityOptions, 'env' | 'cwd'>): string;
|
|
35
|
+
export declare function resolveWorkspaceId(opts?: WorkspaceIdentityOptions): string | null;
|
|
36
|
+
export declare function readWorkspaceIdFromFs(file: string): string | null;
|
|
37
|
+
export declare function writeWorkspaceIdToFs(file: string, id?: string): string | null;
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
import { randomBytes } from 'node:crypto';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { constants, chmodSync, closeSync, lstatSync, mkdirSync, openSync, readFileSync, writeSync } from 'node:fs';
|
|
4
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
5
|
+
export const WORKSPACE_KEYCHAIN_SERVICE = 'evomap.evolver.workspace-id';
|
|
6
|
+
const WORKSPACE_ID_RE = /^[a-f0-9]{32,}$/i;
|
|
7
|
+
const require = createRequire(import.meta.url);
|
|
8
|
+
let cachedAddon;
|
|
9
|
+
export function resetWorkspaceKeychainAddonCacheForTests() {
|
|
10
|
+
cachedAddon = undefined;
|
|
11
|
+
}
|
|
12
|
+
export function getWorkspaceKeychainMode(env = process.env) {
|
|
13
|
+
const raw = String(env['EVOLVER_WORKSPACE_KEYCHAIN'] ?? 'auto').trim().toLowerCase();
|
|
14
|
+
return raw === 'force' || raw === 'off' || raw === 'auto' ? raw : 'auto';
|
|
15
|
+
}
|
|
16
|
+
export function loadWorkspaceKeychainAddon() {
|
|
17
|
+
if (cachedAddon !== undefined)
|
|
18
|
+
return cachedAddon;
|
|
19
|
+
try {
|
|
20
|
+
const mod = require('@napi-rs/keyring');
|
|
21
|
+
cachedAddon = typeof mod.Entry === 'function' ? mod : null;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
cachedAddon = null;
|
|
25
|
+
}
|
|
26
|
+
return cachedAddon;
|
|
27
|
+
}
|
|
28
|
+
const NO_ENTRY_PATTERNS = [
|
|
29
|
+
/no\s+matching\s+entry/i,
|
|
30
|
+
/could\s+not\s+be\s+found/i,
|
|
31
|
+
/element\s+not\s+found/i,
|
|
32
|
+
/\bnoentry\b/i,
|
|
33
|
+
/\bno\s*entry\b/i,
|
|
34
|
+
/not\s+found\s+in\s+(?:secure|keychain)/i,
|
|
35
|
+
];
|
|
36
|
+
export function isWorkspaceKeychainNoEntryError(err) {
|
|
37
|
+
const message = err instanceof Error ? err.message : '';
|
|
38
|
+
return NO_ENTRY_PATTERNS.some((pattern) => pattern.test(message));
|
|
39
|
+
}
|
|
40
|
+
export function readFromWorkspaceKeychain(account, addon = loadWorkspaceKeychainAddon()) {
|
|
41
|
+
if (!addon)
|
|
42
|
+
return { available: false, id: null };
|
|
43
|
+
try {
|
|
44
|
+
const entry = new addon.Entry(WORKSPACE_KEYCHAIN_SERVICE, account);
|
|
45
|
+
const raw = entry.getPassword();
|
|
46
|
+
const id = typeof raw === 'string' ? raw.trim() : '';
|
|
47
|
+
return WORKSPACE_ID_RE.test(id) ? { available: true, id } : { available: true, id: null };
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return isWorkspaceKeychainNoEntryError(err)
|
|
51
|
+
? { available: true, id: null }
|
|
52
|
+
: { available: false, id: null };
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export function writeToWorkspaceKeychain(account, id, addon = loadWorkspaceKeychainAddon()) {
|
|
56
|
+
if (!addon || !WORKSPACE_ID_RE.test(id))
|
|
57
|
+
return false;
|
|
58
|
+
try {
|
|
59
|
+
const entry = new addon.Entry(WORKSPACE_KEYCHAIN_SERVICE, account);
|
|
60
|
+
entry.setPassword(id);
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
export const defaultWorkspaceKeychain = {
|
|
68
|
+
loadAddon: loadWorkspaceKeychainAddon,
|
|
69
|
+
readFromKeychain: (account) => readFromWorkspaceKeychain(account),
|
|
70
|
+
writeToKeychain: (account, id) => writeToWorkspaceKeychain(account, id),
|
|
71
|
+
getMode: getWorkspaceKeychainMode,
|
|
72
|
+
};
|
|
73
|
+
export function workspaceIdPath(workspaceRoot) {
|
|
74
|
+
return join(workspaceRoot, '.evolver', 'workspace-id');
|
|
75
|
+
}
|
|
76
|
+
export function resolveWorkspaceRootForIdentity(opts = {}) {
|
|
77
|
+
const env = opts.env ?? process.env;
|
|
78
|
+
const explicit = env['EVOLVER_REPO_ROOT']?.trim();
|
|
79
|
+
if (explicit)
|
|
80
|
+
return workspaceRootForRepo(resolve(explicit));
|
|
81
|
+
const cwd = resolve(opts.cwd ?? process.cwd());
|
|
82
|
+
const repoRoot = findNearestGitRoot(cwd);
|
|
83
|
+
return workspaceRootForRepo(repoRoot ?? cwd);
|
|
84
|
+
}
|
|
85
|
+
export function resolveWorkspaceId(opts = {}) {
|
|
86
|
+
const env = opts.env ?? process.env;
|
|
87
|
+
const override = env['EVOLVER_WORKSPACE_ID']?.trim();
|
|
88
|
+
if (override)
|
|
89
|
+
return override;
|
|
90
|
+
const workspaceRoot = resolve(opts.workspaceRoot ?? resolveWorkspaceRootForIdentity({ env, cwd: opts.cwd }));
|
|
91
|
+
const file = workspaceIdPath(workspaceRoot);
|
|
92
|
+
const keychain = opts.keychain ?? defaultWorkspaceKeychain;
|
|
93
|
+
const mode = keychain.getMode(env);
|
|
94
|
+
if (mode !== 'off') {
|
|
95
|
+
const addonAvailable = keychain.loadAddon() !== null;
|
|
96
|
+
if (mode === 'force' && !addonAvailable) {
|
|
97
|
+
throw new Error('EVOLVER_WORKSPACE_KEYCHAIN=force but @napi-rs/keyring is not installed or unavailable.');
|
|
98
|
+
}
|
|
99
|
+
if (addonAvailable) {
|
|
100
|
+
const hit = keychain.readFromKeychain(workspaceRoot);
|
|
101
|
+
if (hit.available && hit.id)
|
|
102
|
+
return hit.id;
|
|
103
|
+
if (!hit.available && mode !== 'force') {
|
|
104
|
+
return readWorkspaceIdFromFs(file);
|
|
105
|
+
}
|
|
106
|
+
if (mode === 'force') {
|
|
107
|
+
if (!hit.available) {
|
|
108
|
+
throw new Error('EVOLVER_WORKSPACE_KEYCHAIN=force: keychain reports unavailable; refusing filesystem fallback.');
|
|
109
|
+
}
|
|
110
|
+
const fresh = randomWorkspaceId();
|
|
111
|
+
if (!keychain.writeToKeychain(workspaceRoot, fresh)) {
|
|
112
|
+
throw new Error('EVOLVER_WORKSPACE_KEYCHAIN=force: keychain write failed; refusing filesystem fallback.');
|
|
113
|
+
}
|
|
114
|
+
return fresh;
|
|
115
|
+
}
|
|
116
|
+
const fsId = readWorkspaceIdFromFs(file);
|
|
117
|
+
if (fsId) {
|
|
118
|
+
keychain.writeToKeychain(workspaceRoot, fsId);
|
|
119
|
+
return fsId;
|
|
120
|
+
}
|
|
121
|
+
const fresh = writeWorkspaceIdToFs(file);
|
|
122
|
+
if (!fresh)
|
|
123
|
+
return null;
|
|
124
|
+
keychain.writeToKeychain(workspaceRoot, fresh);
|
|
125
|
+
return fresh;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
const existing = readWorkspaceIdFromFs(file);
|
|
129
|
+
if (existing)
|
|
130
|
+
return existing;
|
|
131
|
+
return writeWorkspaceIdToFs(file);
|
|
132
|
+
}
|
|
133
|
+
export function readWorkspaceIdFromFs(file) {
|
|
134
|
+
return readWorkspaceIdFile(file).id;
|
|
135
|
+
}
|
|
136
|
+
function readWorkspaceIdFile(file) {
|
|
137
|
+
try {
|
|
138
|
+
const dirStat = safeLstat(dirname(file));
|
|
139
|
+
if (dirStat?.isSymbolicLink())
|
|
140
|
+
return { id: null, repairableInvalid: false };
|
|
141
|
+
const fileStat = safeLstat(file);
|
|
142
|
+
if (!fileStat || fileStat.isSymbolicLink() || !fileStat.isFile())
|
|
143
|
+
return { id: null, repairableInvalid: false };
|
|
144
|
+
const raw = readFileSync(file, 'utf8').trim();
|
|
145
|
+
if (WORKSPACE_ID_RE.test(raw))
|
|
146
|
+
return { id: raw, repairableInvalid: false };
|
|
147
|
+
return { id: null, repairableInvalid: raw.length >= 32 };
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return { id: null, repairableInvalid: false };
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
export function writeWorkspaceIdToFs(file, id = randomWorkspaceId()) {
|
|
154
|
+
if (!WORKSPACE_ID_RE.test(id))
|
|
155
|
+
return null;
|
|
156
|
+
try {
|
|
157
|
+
const dir = dirname(file);
|
|
158
|
+
const dirStat = safeLstat(dir);
|
|
159
|
+
if (dirStat?.isSymbolicLink())
|
|
160
|
+
return null;
|
|
161
|
+
mkdirSync(dir, { recursive: true });
|
|
162
|
+
const flags = constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | (constants.O_NOFOLLOW ?? 0);
|
|
163
|
+
let fd;
|
|
164
|
+
try {
|
|
165
|
+
fd = openSync(file, flags, 0o600);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
if (err.code !== 'EEXIST')
|
|
169
|
+
return null;
|
|
170
|
+
const existing = readWorkspaceIdFile(file);
|
|
171
|
+
if (existing.id)
|
|
172
|
+
return existing.id;
|
|
173
|
+
return existing.repairableInvalid ? repairInvalidWorkspaceIdFile(file, id) : null;
|
|
174
|
+
}
|
|
175
|
+
try {
|
|
176
|
+
writeSync(fd, `${id}\n`, 0, 'utf8');
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
closeSync(fd);
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
chmodSync(file, 0o600);
|
|
183
|
+
}
|
|
184
|
+
catch { /* best-effort on Windows */ }
|
|
185
|
+
return id;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return null;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function repairInvalidWorkspaceIdFile(file, id) {
|
|
192
|
+
try {
|
|
193
|
+
const dirStat = safeLstat(dirname(file));
|
|
194
|
+
if (dirStat?.isSymbolicLink())
|
|
195
|
+
return null;
|
|
196
|
+
const fileStat = safeLstat(file);
|
|
197
|
+
if (!fileStat || fileStat.isSymbolicLink() || !fileStat.isFile())
|
|
198
|
+
return null;
|
|
199
|
+
const fd = openWorkspaceIdForRepair(file);
|
|
200
|
+
try {
|
|
201
|
+
writeSync(fd, `${id}\n`, 0, 'utf8');
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
closeSync(fd);
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
chmodSync(file, 0o600);
|
|
208
|
+
}
|
|
209
|
+
catch { /* best-effort on Windows */ }
|
|
210
|
+
return id;
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function openWorkspaceIdForRepair(file) {
|
|
217
|
+
const noFollow = constants.O_NOFOLLOW;
|
|
218
|
+
if (typeof noFollow === 'number') {
|
|
219
|
+
return openSync(file, constants.O_WRONLY | constants.O_TRUNC | noFollow);
|
|
220
|
+
}
|
|
221
|
+
return openSync(file, 'w', 0o600);
|
|
222
|
+
}
|
|
223
|
+
function randomWorkspaceId() {
|
|
224
|
+
return randomBytes(16).toString('hex');
|
|
225
|
+
}
|
|
226
|
+
function safeLstat(path) {
|
|
227
|
+
try {
|
|
228
|
+
return lstatSync(path);
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function findNearestGitRoot(start) {
|
|
235
|
+
let dir = resolve(start);
|
|
236
|
+
for (;;) {
|
|
237
|
+
if (safeLstat(join(dir, '.git')))
|
|
238
|
+
return dir;
|
|
239
|
+
if (basename(dir) === 'node_modules')
|
|
240
|
+
return null;
|
|
241
|
+
const parent = dirname(dir);
|
|
242
|
+
if (parent === dir)
|
|
243
|
+
return null;
|
|
244
|
+
dir = parent;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
function workspaceRootForRepo(repoRoot) {
|
|
248
|
+
const workspace = join(repoRoot, 'workspace');
|
|
249
|
+
const st = safeLstat(workspace);
|
|
250
|
+
return st?.isDirectory() ? workspace : repoRoot;
|
|
251
|
+
}
|
package/dist/connect.js
CHANGED
|
@@ -6,6 +6,7 @@ import { LegacyAuthShim } from './auth/legacyShim.js';
|
|
|
6
6
|
import { PublicOAuthProvider } from './auth/oauthDeviceToken.js';
|
|
7
7
|
import { createOAuthHttpTransport } from './auth/oauthHttpTransport.js';
|
|
8
8
|
import { KeypairProvider } from './auth/keypair.js';
|
|
9
|
+
import { resolveWorkspaceId, resolveWorkspaceRootForIdentity } from './auth/workspaceKeychain.js';
|
|
9
10
|
/** 选址装配公版 hub(M6-7): 按 authMode 组 AuthProvider(M6-5) + PublicHubCapability(M6-6). */
|
|
10
11
|
export function connectPublicHub(opts) {
|
|
11
12
|
const dir = opts.evomapDir ?? join(homedir(), '.evomap');
|
|
@@ -34,5 +35,18 @@ export function connectPublicHub(opts) {
|
|
|
34
35
|
throw new Error(`未知 authMode: ${String(_x)}`);
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
|
-
return { hub: new PublicHubCapability({ baseUrl: opts.hubUrl, auth, fetchFn, senderId: opts.senderId, antiAbuse: opts.antiAbuse }), auth };
|
|
38
|
+
return { hub: new PublicHubCapability({ baseUrl: opts.hubUrl, auth, fetchFn, senderId: opts.senderId, antiAbuse: withWorkspaceIdResolver(opts.antiAbuse) }), auth };
|
|
39
|
+
}
|
|
40
|
+
function withWorkspaceIdResolver(antiAbuse) {
|
|
41
|
+
if (antiAbuse?.workspaceId || antiAbuse?.workspaceIdResolver)
|
|
42
|
+
return antiAbuse;
|
|
43
|
+
const env = antiAbuse?.env ?? process.env;
|
|
44
|
+
let workspaceRoot;
|
|
45
|
+
return {
|
|
46
|
+
...(antiAbuse ?? {}),
|
|
47
|
+
workspaceIdResolver: () => {
|
|
48
|
+
workspaceRoot ??= resolveWorkspaceRootForIdentity({ env });
|
|
49
|
+
return resolveWorkspaceId({ env, workspaceRoot });
|
|
50
|
+
},
|
|
51
|
+
};
|
|
38
52
|
}
|
package/dist/hubCapability.d.ts
CHANGED
|
@@ -30,6 +30,32 @@ export interface OutcomeReceipt {
|
|
|
30
30
|
recorded: boolean;
|
|
31
31
|
reason?: string;
|
|
32
32
|
}
|
|
33
|
+
export type MemoryGraphEventKind = 'attempt' | 'validation' | 'skill_emit' | 'outcome' | 'mutation_draft' | 'solidify';
|
|
34
|
+
export interface MemoryGraphEventReport {
|
|
35
|
+
kind: MemoryGraphEventKind;
|
|
36
|
+
event: Record<string, unknown>;
|
|
37
|
+
}
|
|
38
|
+
/** recordMemoryEvent never throws: failures come back as `recorded:false` + reason. */
|
|
39
|
+
export interface MemoryGraphEventReceipt {
|
|
40
|
+
recorded: boolean;
|
|
41
|
+
reason?: string;
|
|
42
|
+
}
|
|
43
|
+
export type AccountAssetScope = 'purchased' | 'published';
|
|
44
|
+
export type AccountAssetTypeFilter = 'Gene' | 'Capsule';
|
|
45
|
+
export type AccountAssetStatusFilter = 'draft' | 'promoted' | 'all';
|
|
46
|
+
export interface AccountAssetListOptions {
|
|
47
|
+
scope: AccountAssetScope;
|
|
48
|
+
type?: AccountAssetTypeFilter;
|
|
49
|
+
status?: AccountAssetStatusFilter;
|
|
50
|
+
limit?: number;
|
|
51
|
+
cursor?: string;
|
|
52
|
+
}
|
|
53
|
+
export interface AccountAssetListResult {
|
|
54
|
+
assets: hub.AssetRecord[];
|
|
55
|
+
count?: number;
|
|
56
|
+
hasMore: boolean;
|
|
57
|
+
nextCursor?: string;
|
|
58
|
+
}
|
|
33
59
|
/** 完整 GEP-A2A 信封(实测 dev: publish/fetch/validate 等协议消息端点必须全信封, 非仅 protocol+message_type). */
|
|
34
60
|
export declare function gepEnvelope(messageType: string, payload: unknown): Record<string, unknown>;
|
|
35
61
|
export interface PublicHubOptions {
|
|
@@ -117,6 +143,8 @@ export declare class PublicHubCapability implements hub.HubCapability {
|
|
|
117
143
|
* signals/id queries fall through to fetch. /a2a/fetch does NOT do semantic, so text must not go there.
|
|
118
144
|
*/
|
|
119
145
|
search(query: hub.HubQuery): Promise<hub.AssetRecord[]>;
|
|
146
|
+
agentDirectory: hub.AgentDirectoryCapability;
|
|
147
|
+
listAccountAssets(opts: AccountAssetListOptions): Promise<AccountAssetListResult>;
|
|
120
148
|
/**
|
|
121
149
|
* Report a cycle outcome to the hub's memory graph (POST /a2a/memory/record).
|
|
122
150
|
* Unlike the protocol-message endpoints (publish/fetch), memory/record takes a
|
|
@@ -126,6 +154,7 @@ export declare class PublicHubCapability implements hub.HubCapability {
|
|
|
126
154
|
* Costs hub credits per the hub's memory pricing (caller gates on enablement).
|
|
127
155
|
*/
|
|
128
156
|
recordOutcome(report: OutcomeReport): Promise<OutcomeReceipt>;
|
|
157
|
+
recordMemoryEvent(report: MemoryGraphEventReport): Promise<MemoryGraphEventReceipt>;
|
|
129
158
|
recordReuseResult(report: hub.ReuseResultReport): Promise<hub.ReuseResultReceipt>;
|
|
130
159
|
/**
|
|
131
160
|
* Pre-publish dry-run (POST /a2a/validate). The hub runs the same hub-side quality +
|
|
@@ -146,7 +175,7 @@ export declare class PublicHubCapability implements hub.HubCapability {
|
|
|
146
175
|
claim: (taskId: string) => Promise<{
|
|
147
176
|
claimId: string;
|
|
148
177
|
}>;
|
|
149
|
-
complete: (claimId: string,
|
|
178
|
+
complete: (claimId: string, _result: unknown, context?: hub.TaskCompleteContext) => Promise<{
|
|
150
179
|
status: "completed";
|
|
151
180
|
}>;
|
|
152
181
|
subscribe: (filter: unknown) => AsyncIterable<hub.TaskEvent>;
|
package/dist/hubCapability.js
CHANGED
|
@@ -3,11 +3,13 @@ import { AuthError, HubFetch, HubClientError, isHubUnreachableError } from './hu
|
|
|
3
3
|
import { isNodeSecret, parseNodeSecretVersion } from './auth/legacyShim.js';
|
|
4
4
|
import { inboundToAgentEvent, agentEventToOutbound, publishRespToReceipt, searchQueryToFetchWire } from './wireMap.js';
|
|
5
5
|
import { antiAbuseTelemetryMode, buildHeartbeatAntiAbuseTelemetry, } from './antiAbuseTelemetry.js';
|
|
6
|
+
import { getWorkspaceKeychainMode } from './auth/workspaceKeychain.js';
|
|
7
|
+
import { agentDirectoryFailure, parsePublicAgentPage, parsePublicAgentProfile, paginatePublicAgentPage, mergePublicAgentPages, publicAgentSearchQuery, publicTaskDiscoveryQuery, PUBLIC_TASK_DISCOVERY_MAX_CANDIDATES, unsupportedPublicAvailability, unsupportedPublicSort, withDirectoryTimeout, } from './agentDirectory.js';
|
|
6
8
|
export const INBOUND_LIMIT = 100;
|
|
7
9
|
export const OUTBOUND_MAX_BATCH = 50;
|
|
8
10
|
export const OUTBOUND_MAX_BODY_BYTES = 4 * 1024 * 1024;
|
|
9
11
|
export const PUBLIC_PROTOCOL_VERSION = 'gep-a2a/1.0.0';
|
|
10
|
-
export const PUBLIC_HUB_CAPABILITIES = ['publish', 'fetch', 'search', 'task', 'mailbox', 'auth', 'marketplace', 'economy', 'questions', 'recipes'];
|
|
12
|
+
export const PUBLIC_HUB_CAPABILITIES = ['publish', 'fetch', 'search', 'task', 'mailbox', 'auth', 'marketplace', 'economy', 'questions', 'recipes', 'agent_directory'];
|
|
11
13
|
const QUESTION_SUBMIT_FAST_PATH_BYPASS_CONTENT_HASH = 'sha256:0000000000000000000000000000000000000000000000000000000000000000';
|
|
12
14
|
const DRY_RUN_RECIPE_ID = 'dry-run-recipe';
|
|
13
15
|
const HUB_DRY_RUN_VALUES = new Set(['1', 'true', 'yes', 'on']);
|
|
@@ -194,7 +196,9 @@ export class PublicHubCapability {
|
|
|
194
196
|
evolverVersion: opts.evolverVersion,
|
|
195
197
|
});
|
|
196
198
|
}
|
|
197
|
-
catch {
|
|
199
|
+
catch (err) {
|
|
200
|
+
if (getWorkspaceKeychainMode(antiAbuse.env) === 'force')
|
|
201
|
+
throw err;
|
|
198
202
|
process.stderr.write('[anti-abuse] failed to build heartbeat telemetry; continuing without heartbeat meta\n');
|
|
199
203
|
}
|
|
200
204
|
}
|
|
@@ -241,6 +245,78 @@ export class PublicHubCapability {
|
|
|
241
245
|
}
|
|
242
246
|
return this.fetch(query);
|
|
243
247
|
}
|
|
248
|
+
agentDirectory = {
|
|
249
|
+
search: async (request) => {
|
|
250
|
+
try {
|
|
251
|
+
const normalized = hubNs.normalizeAgentSearchRequest(request);
|
|
252
|
+
const unsupported = unsupportedPublicAvailability(normalized.availability) ?? unsupportedPublicSort(normalized.sort);
|
|
253
|
+
if (unsupported)
|
|
254
|
+
return unsupported;
|
|
255
|
+
const body = await withDirectoryTimeout(this.http.call('GET', '/a2a/directory/search', undefined, publicAgentSearchQuery(normalized)), normalized.timeoutMs);
|
|
256
|
+
return paginatePublicAgentPage(parsePublicAgentPage(body), normalized);
|
|
257
|
+
}
|
|
258
|
+
catch (error) {
|
|
259
|
+
return agentDirectoryFailure(error);
|
|
260
|
+
}
|
|
261
|
+
},
|
|
262
|
+
getProfile: async (agentId, options) => {
|
|
263
|
+
try {
|
|
264
|
+
const normalizedId = hubNs.normalizeAgentId(agentId);
|
|
265
|
+
const timeoutMs = hubNs.normalizeAgentDirectoryTimeout(options?.timeoutMs);
|
|
266
|
+
const body = await withDirectoryTimeout(this.http.call('GET', `/a2a/directory/profile/${encodeURIComponent(normalizedId)}`), timeoutMs);
|
|
267
|
+
return parsePublicAgentProfile(body);
|
|
268
|
+
}
|
|
269
|
+
catch (error) {
|
|
270
|
+
if (error instanceof HubClientError && error.status === 404)
|
|
271
|
+
return { ok: true, value: null };
|
|
272
|
+
return agentDirectoryFailure(error);
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
discoverForTask: async (request) => {
|
|
276
|
+
try {
|
|
277
|
+
const normalized = hubNs.normalizeAgentTaskDiscoveryRequest(request);
|
|
278
|
+
const unsupported = unsupportedPublicAvailability(normalized.availability) ?? unsupportedPublicSort(normalized.sort);
|
|
279
|
+
if (unsupported)
|
|
280
|
+
return unsupported;
|
|
281
|
+
const taskQuery = [normalized.title, normalized.description].filter(Boolean).join('\n');
|
|
282
|
+
const searches = [
|
|
283
|
+
this.http.call('GET', '/a2a/directory/search', undefined, publicAgentSearchQuery({
|
|
284
|
+
query: taskQuery,
|
|
285
|
+
...(normalized.availability ? { availability: normalized.availability } : {}),
|
|
286
|
+
})),
|
|
287
|
+
];
|
|
288
|
+
if (normalized.signals && normalized.signals.length > 0) {
|
|
289
|
+
searches.push(this.http.call('GET', '/a2a/directory/search', undefined, publicTaskDiscoveryQuery(normalized)));
|
|
290
|
+
}
|
|
291
|
+
const bodies = await withDirectoryTimeout(Promise.all(searches), normalized.timeoutMs);
|
|
292
|
+
return paginatePublicAgentPage(mergePublicAgentPages(bodies.map(parsePublicAgentPage)), normalized, searches.length > 1 ? PUBLIC_TASK_DISCOVERY_MAX_CANDIDATES : hubNs.AGENT_DIRECTORY_MAX_LIMIT);
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
return agentDirectoryFailure(error);
|
|
296
|
+
}
|
|
297
|
+
},
|
|
298
|
+
};
|
|
299
|
+
async listAccountAssets(opts) {
|
|
300
|
+
const path = opts.scope === 'purchased' ? '/a2a/assets/purchased' : '/a2a/assets/published-by-me';
|
|
301
|
+
const query = {
|
|
302
|
+
...(opts.limit !== undefined ? { limit: opts.limit } : {}),
|
|
303
|
+
...(opts.cursor ? { cursor: opts.cursor } : {}),
|
|
304
|
+
...(opts.type ? { type: opts.type } : {}),
|
|
305
|
+
...(opts.scope === 'published' && opts.status && opts.status !== 'all' ? { status: opts.status } : {}),
|
|
306
|
+
};
|
|
307
|
+
const body = await this.http.call('GET', path, undefined, query);
|
|
308
|
+
const payload = asRecord(body['payload']) ?? body;
|
|
309
|
+
const assets = accountAssetsFromPayload(payload);
|
|
310
|
+
const count = numberField(payload, 'count');
|
|
311
|
+
const nextCursor = stringField(payload, 'next_cursor') ?? stringField(payload, 'nextCursor');
|
|
312
|
+
const hasMore = booleanField(payload, 'has_more') ?? booleanField(payload, 'hasMore') ?? Boolean(nextCursor);
|
|
313
|
+
return {
|
|
314
|
+
assets,
|
|
315
|
+
...(count !== undefined ? { count } : {}),
|
|
316
|
+
hasMore,
|
|
317
|
+
...(nextCursor ? { nextCursor } : {}),
|
|
318
|
+
};
|
|
319
|
+
}
|
|
244
320
|
/**
|
|
245
321
|
* Report a cycle outcome to the hub's memory graph (POST /a2a/memory/record).
|
|
246
322
|
* Unlike the protocol-message endpoints (publish/fetch), memory/record takes a
|
|
@@ -269,6 +345,24 @@ export class PublicHubCapability {
|
|
|
269
345
|
return { recorded: false, reason: e instanceof Error ? e.message : String(e) };
|
|
270
346
|
}
|
|
271
347
|
}
|
|
348
|
+
async recordMemoryEvent(report) {
|
|
349
|
+
const sender = this.opts.senderId()?.trim();
|
|
350
|
+
if (!sender)
|
|
351
|
+
return { recorded: false, reason: 'sender_id_required' };
|
|
352
|
+
const event = asRecord(report.event);
|
|
353
|
+
if (!event)
|
|
354
|
+
return { recorded: false, reason: 'event_required' };
|
|
355
|
+
try {
|
|
356
|
+
await this.http.call('POST', '/a2a/memory/event', {
|
|
357
|
+
sender_id: sender,
|
|
358
|
+
event: { ...event, kind: report.kind },
|
|
359
|
+
});
|
|
360
|
+
return { recorded: true };
|
|
361
|
+
}
|
|
362
|
+
catch (e) {
|
|
363
|
+
return { recorded: false, reason: e instanceof Error ? e.message : String(e) };
|
|
364
|
+
}
|
|
365
|
+
}
|
|
272
366
|
async recordReuseResult(report) {
|
|
273
367
|
const assetId = report.assetId.trim();
|
|
274
368
|
if (!assetId)
|
|
@@ -398,11 +492,31 @@ export class PublicHubCapability {
|
|
|
398
492
|
}
|
|
399
493
|
task = {
|
|
400
494
|
claim: async (taskId) => {
|
|
401
|
-
|
|
402
|
-
|
|
495
|
+
const event = {
|
|
496
|
+
id: `claim-${taskId}`,
|
|
497
|
+
type: 'task_claim',
|
|
498
|
+
payload: { task_id: taskId },
|
|
499
|
+
priority: 'medium',
|
|
500
|
+
createdAt: Date.now(),
|
|
501
|
+
};
|
|
502
|
+
const body = await this.http.call('POST', '/a2a/mailbox/outbound', { messages: [{ id: event.id, type: event.type, payload: event.payload }] });
|
|
503
|
+
assertMailboxPushAccepted(mailboxPushResultFromBody(body, [event]).outcomes[0]);
|
|
504
|
+
return { claimId: event.id };
|
|
403
505
|
},
|
|
404
|
-
complete: async (claimId,
|
|
405
|
-
|
|
506
|
+
complete: async (claimId, _result, context) => {
|
|
507
|
+
if (typeof context?.taskId !== 'string' || !context.taskId.trim()
|
|
508
|
+
|| typeof context.assetId !== 'string' || !context.assetId.trim()) {
|
|
509
|
+
throw new hubNs.PublishRejectedError('invalid_task_completion', true, 'task completion requires explicit taskId and assetId', undefined, false);
|
|
510
|
+
}
|
|
511
|
+
const event = {
|
|
512
|
+
id: `complete-${claimId}`,
|
|
513
|
+
type: 'task_complete',
|
|
514
|
+
payload: { task_id: context.taskId, asset_id: context.assetId },
|
|
515
|
+
priority: 'medium',
|
|
516
|
+
createdAt: Date.now(),
|
|
517
|
+
};
|
|
518
|
+
const body = await this.http.call('POST', '/a2a/mailbox/outbound', { messages: [{ id: event.id, type: event.type, payload: event.payload }] });
|
|
519
|
+
assertMailboxPushAccepted(mailboxPushResultFromBody(body, [event]).outcomes[0]);
|
|
406
520
|
return { status: 'completed' };
|
|
407
521
|
},
|
|
408
522
|
subscribe: (filter) => this.subscribeTasks(filter),
|
|
@@ -449,10 +563,7 @@ export class PublicHubCapability {
|
|
|
449
563
|
ack: async (eventId) => { await this.http.call('POST', '/a2a/mailbox/ack', { message_ids: [eventId] }); },
|
|
450
564
|
push: async (event) => {
|
|
451
565
|
const result = await this.mailbox.pushMany([event]);
|
|
452
|
-
|
|
453
|
-
if (outcome?.status !== 'failed')
|
|
454
|
-
return;
|
|
455
|
-
throw new hubNs.PublishRejectedError('mailbox_push_rejected', outcome.terminal ?? outcome.retryable !== true, outcome.reason ?? 'mailbox_push_rejected', outcome.retryAfterMs, outcome.retryable);
|
|
566
|
+
assertMailboxPushAccepted(result?.outcomes.find((item) => item.id === event.id));
|
|
456
567
|
},
|
|
457
568
|
pushMany: async (events) => {
|
|
458
569
|
if (events.length === 0)
|
|
@@ -601,6 +712,19 @@ function assetsFromBody(body) {
|
|
|
601
712
|
];
|
|
602
713
|
return candidates.filter((candidate) => Boolean(candidate && typeof candidate === 'object' && !Array.isArray(candidate)));
|
|
603
714
|
}
|
|
715
|
+
function accountAssetsFromPayload(payload) {
|
|
716
|
+
const candidates = [
|
|
717
|
+
payload['assets'],
|
|
718
|
+
payload['results'],
|
|
719
|
+
payload['items'],
|
|
720
|
+
];
|
|
721
|
+
for (const candidate of candidates) {
|
|
722
|
+
if (!Array.isArray(candidate))
|
|
723
|
+
continue;
|
|
724
|
+
return candidate.filter((asset) => Boolean(asset && typeof asset === 'object' && !Array.isArray(asset)));
|
|
725
|
+
}
|
|
726
|
+
return [];
|
|
727
|
+
}
|
|
604
728
|
function assetMatchesId(asset, assetId) {
|
|
605
729
|
return Boolean(asset && (asset.asset_id === assetId || stringField(asset, 'id') === assetId));
|
|
606
730
|
}
|
|
@@ -666,6 +790,11 @@ function splitMailboxOutboundBatches(events) {
|
|
|
666
790
|
out.push({ events: batch });
|
|
667
791
|
return out;
|
|
668
792
|
}
|
|
793
|
+
function assertMailboxPushAccepted(outcome) {
|
|
794
|
+
if (outcome?.status !== 'failed')
|
|
795
|
+
return;
|
|
796
|
+
throw new hubNs.PublishRejectedError('mailbox_push_rejected', outcome.terminal ?? outcome.retryable !== true, outcome.reason ?? 'mailbox_push_rejected', outcome.retryAfterMs, outcome.retryable);
|
|
797
|
+
}
|
|
669
798
|
function mailboxPushResultFromBody(body, events) {
|
|
670
799
|
const results = mailboxResultRows(body);
|
|
671
800
|
if (results.length === 0) {
|
|
@@ -690,16 +819,32 @@ function mailboxPushOutcomeFromRow(eventId, results, index) {
|
|
|
690
819
|
})();
|
|
691
820
|
const retryable = booleanField(match, 'retryable');
|
|
692
821
|
const terminal = booleanField(match, 'terminal');
|
|
822
|
+
const inferredTerminal = retryable === undefined && terminal === undefined
|
|
823
|
+
? isKnownTerminalMailboxFailure(match, reason)
|
|
824
|
+
: undefined;
|
|
693
825
|
return {
|
|
694
826
|
id: eventId,
|
|
695
827
|
status: 'failed',
|
|
696
828
|
reason,
|
|
697
|
-
...(retryable !== undefined ? { retryable } : {}),
|
|
698
|
-
...(terminal !== undefined ? { terminal } : {}),
|
|
829
|
+
...(retryable !== undefined ? { retryable } : inferredTerminal === false ? { retryable: true } : {}),
|
|
830
|
+
...(terminal !== undefined ? { terminal } : inferredTerminal !== undefined ? { terminal: inferredTerminal } : {}),
|
|
699
831
|
...(retryAfterMs !== undefined ? { retryAfterMs } : {}),
|
|
700
832
|
raw: match,
|
|
701
833
|
};
|
|
702
834
|
}
|
|
835
|
+
function isKnownTerminalMailboxFailure(row, reason) {
|
|
836
|
+
const status = stringField(row, 'status')?.toLowerCase() ?? '';
|
|
837
|
+
if (status.includes('reject') || status.includes('invalid') || status === 'quarantine')
|
|
838
|
+
return true;
|
|
839
|
+
const normalizedReason = reason.toLowerCase();
|
|
840
|
+
const alreadySubmittedCode = 'already_submitted_for_task';
|
|
841
|
+
if (normalizedReason === alreadySubmittedCode
|
|
842
|
+
|| normalizedReason.startsWith(`${alreadySubmittedCode}:`)
|
|
843
|
+
|| normalizedReason === 'orphan_node_not_allowed_for_bounty')
|
|
844
|
+
return true;
|
|
845
|
+
return /\binvalid[_ -]|validation[_ -]?error|payload[_ -]?too[_ -]?large|not[_ -]?found|expired|already[_ -]?(claimed|completed)|duplicate|conflict|not[_ -]?(task[_ -]?(owner|claimer)|asset[_ -]?owner|open|active|claimed|eligible)|asset[_ -]?(orphaned|not[_ -]?promoted)|task[_ -]?full|node[_ -]?(suspended|merging|dead)|insufficient[_ -]?(reputation|model[_ -]?tier)|task[_ -]?reserved[_ -]?for[_ -]?preferred[_ -]?merchant|atp[_ -]?requires[_ -]?evolver[_ -]?version|self[_ -]?funded[_ -]?bounty[_ -]?self[_ -]?claim/i
|
|
846
|
+
.test(reason);
|
|
847
|
+
}
|
|
703
848
|
// A 413 (payload too large) or 429 (rate-limited) can surface either as a JSON
|
|
704
849
|
// HubClientError, OR — when an ingress/gateway (Envoy/nginx) emits a non-JSON
|
|
705
850
|
// body — as a HubUnreachableError carrying the HTTP status in `details.status`
|
package/dist/hubReuse.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { hub } from '@evomap/evolver-core';
|
|
2
|
-
import type { algo } from '@evomap/evolver-core';
|
|
1
|
+
import { hub, algo } from '@evomap/evolver-core';
|
|
3
2
|
type HubMetadata = hub.HubMetadata;
|
|
4
3
|
type ReuseDecision = hub.ReuseDecision;
|
|
5
4
|
type GeneCandidateInput = algo.GeneCandidateInput;
|
package/dist/hubReuse.js
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
// - search cache: signal-fingerprint → phase-1 metadata (short TTL). Repeat signal set → ZERO hub calls.
|
|
13
13
|
// - payload cache: assetId → phase-3 payload (content-addressed, long/permanent, bounded LRU). A cached
|
|
14
14
|
// payload → ZERO fetch. Both clocks are injected so TTL/eviction is deterministic and testable.
|
|
15
|
-
import { hub } from '@evomap/evolver-core';
|
|
15
|
+
import { hub, algo } from '@evomap/evolver-core';
|
|
16
16
|
const { scoreSearchResults, decideReuse, DEFAULT_MIN_REUSE_SCORE, } = hub;
|
|
17
17
|
const GENE_WIRE_KEYS = new Set([
|
|
18
18
|
'type',
|
|
@@ -30,6 +30,7 @@ const GENE_WIRE_KEYS = new Set([
|
|
|
30
30
|
'anti_patterns',
|
|
31
31
|
'routing_hint',
|
|
32
32
|
'tool_policy',
|
|
33
|
+
'generation_meta',
|
|
33
34
|
'asset_id',
|
|
34
35
|
]);
|
|
35
36
|
// ── Cache config (ported from v1 hubSearch.js) ───────────────────────────────
|
|
@@ -163,6 +164,7 @@ export function toGeneCandidate(rec) {
|
|
|
163
164
|
const geneId = typeof r['id'] === 'string' ? r['id'] : assetId;
|
|
164
165
|
const signalsMatch = strArr(r['signals_match'] ?? r['signalsMatch']) ?? [];
|
|
165
166
|
const reuseCount = num(r['reuse_count'] ?? r['reuseCount']) ?? 0;
|
|
167
|
+
const generationSource = algo.geneGenerationSource(r, geneId);
|
|
166
168
|
return {
|
|
167
169
|
geneId,
|
|
168
170
|
assetId,
|
|
@@ -173,6 +175,7 @@ export function toGeneCandidate(rec) {
|
|
|
173
175
|
reuseCount,
|
|
174
176
|
...(typeof r['category'] === 'string' ? { category: r['category'] } : {}),
|
|
175
177
|
...(typeof r['summary'] === 'string' ? { summary: r['summary'] } : {}),
|
|
178
|
+
...(generationSource ? { generationSource } : {}),
|
|
176
179
|
hubAsset: stripHubPayloadMetadata(rec),
|
|
177
180
|
};
|
|
178
181
|
}
|
package/dist/hubUrl.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export declare const DEFAULT_PUBLIC_HUB_URL = "https://evomap.ai";
|
|
2
|
+
export type HubUrlEnv = Record<string, string | undefined>;
|
|
3
|
+
export declare function resolveConfiguredHubUrl(env: HubUrlEnv): string | undefined;
|
|
4
|
+
export declare function resolveHubUrl(env: HubUrlEnv): string;
|
package/dist/hubUrl.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export const DEFAULT_PUBLIC_HUB_URL = 'https://evomap.ai';
|
|
2
|
+
const HUB_URL_KEYS = ['A2A_HUB_URL', 'EVOMAP_HUB_URL', 'EVOLVER_DEFAULT_HUB_URL'];
|
|
3
|
+
function normalizeHubUrl(value) {
|
|
4
|
+
const trimmed = value?.trim();
|
|
5
|
+
if (!trimmed)
|
|
6
|
+
return undefined;
|
|
7
|
+
const normalized = trimmed.replace(/\/+$/, '');
|
|
8
|
+
return normalized || undefined;
|
|
9
|
+
}
|
|
10
|
+
export function resolveConfiguredHubUrl(env) {
|
|
11
|
+
for (const key of HUB_URL_KEYS) {
|
|
12
|
+
const resolved = normalizeHubUrl(env[key]);
|
|
13
|
+
if (resolved)
|
|
14
|
+
return resolved;
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
export function resolveHubUrl(env) {
|
|
19
|
+
return resolveConfiguredHubUrl(env) ?? DEFAULT_PUBLIC_HUB_URL;
|
|
20
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export declare const PACKAGE = "@evomap/evolver-adapter-public";
|
|
2
2
|
export * from './auth/machineId.js';
|
|
3
|
+
export * from './auth/workspaceKeychain.js';
|
|
3
4
|
export * from './auth/credentialStore.js';
|
|
4
5
|
export * from './auth/keypair.js';
|
|
5
6
|
export * from './auth/legacyShim.js';
|
|
@@ -8,9 +9,11 @@ export * from './auth/oauthHttpTransport.js';
|
|
|
8
9
|
export * from './hubFetch.js';
|
|
9
10
|
export * from './wireMap.js';
|
|
10
11
|
export * from './hubCapability.js';
|
|
12
|
+
export * from './agentDirectory.js';
|
|
11
13
|
export * from './antiAbuseTelemetry.js';
|
|
12
14
|
export * from './offlinePermit.js';
|
|
13
15
|
export * from './hubReuse.js';
|
|
16
|
+
export * from './hubUrl.js';
|
|
14
17
|
export * from './atp.js';
|
|
15
18
|
export * from './pricing/modelPrices.js';
|
|
16
19
|
export * from './connect.js';
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export const PACKAGE = '@evomap/evolver-adapter-public';
|
|
2
2
|
export * from './auth/machineId.js';
|
|
3
|
+
export * from './auth/workspaceKeychain.js';
|
|
3
4
|
export * from './auth/credentialStore.js';
|
|
4
5
|
export * from './auth/keypair.js';
|
|
5
6
|
export * from './auth/legacyShim.js';
|
|
@@ -8,9 +9,11 @@ export * from './auth/oauthHttpTransport.js';
|
|
|
8
9
|
export * from './hubFetch.js';
|
|
9
10
|
export * from './wireMap.js';
|
|
10
11
|
export * from './hubCapability.js';
|
|
12
|
+
export * from './agentDirectory.js';
|
|
11
13
|
export * from './antiAbuseTelemetry.js';
|
|
12
14
|
export * from './offlinePermit.js';
|
|
13
15
|
export * from './hubReuse.js';
|
|
16
|
+
export * from './hubUrl.js';
|
|
14
17
|
export * from './atp.js';
|
|
15
18
|
export * from './pricing/modelPrices.js';
|
|
16
19
|
export * from './connect.js';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@evomap/evolver-adapter-public",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "公版 hub 适配器 (积分/治理)",
|
|
@@ -14,15 +14,23 @@
|
|
|
14
14
|
},
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"@evomap/atp-sdk": "^0.1.0",
|
|
17
|
-
"@evomap/evolver-core": "2.0.0-beta.
|
|
17
|
+
"@evomap/evolver-core": "2.0.0-beta.4",
|
|
18
18
|
"undici": "^6.27.0"
|
|
19
19
|
},
|
|
20
|
+
"optionalDependencies": {
|
|
21
|
+
"@napi-rs/keyring": "^1.1.6"
|
|
22
|
+
},
|
|
23
|
+
"repository": {
|
|
24
|
+
"type": "git",
|
|
25
|
+
"url": "git+https://github.com/EvoMap/evolver.git"
|
|
26
|
+
},
|
|
20
27
|
"publishConfig": {
|
|
21
28
|
"access": "public",
|
|
22
29
|
"tag": "v2-beta"
|
|
23
30
|
},
|
|
24
31
|
"files": [
|
|
25
32
|
"dist/",
|
|
33
|
+
"assets/",
|
|
26
34
|
"README.md",
|
|
27
35
|
"package.json"
|
|
28
36
|
]
|