@ainecto/mcp 0.12.0 → 0.12.1
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 +1 -1
- package/dist/bin/ainecto.js +341 -245
- package/dist/bin/ainecto.js.map +1 -1
- package/dist/bin/mcp.js +263 -183
- package/dist/bin/mcp.js.map +1 -1
- package/package.json +2 -2
package/dist/bin/ainecto.js
CHANGED
|
@@ -175,6 +175,259 @@ function isRecord(value) {
|
|
|
175
175
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
// src/core/http/rateLimit.ts
|
|
179
|
+
var RATE_LIMITED = "RATE_LIMITED";
|
|
180
|
+
function parseRetryAfter(value, now = Date.now()) {
|
|
181
|
+
if (value === null || value === void 0) return void 0;
|
|
182
|
+
const trimmed = value.trim();
|
|
183
|
+
if (/^\d+$/.test(trimmed)) return representable(Number(trimmed), now);
|
|
184
|
+
if (!/[a-z]/i.test(trimmed)) return void 0;
|
|
185
|
+
const at = Date.parse(trimmed);
|
|
186
|
+
if (Number.isNaN(at)) return void 0;
|
|
187
|
+
return Math.max(0, Math.ceil((at - now) / 1e3));
|
|
188
|
+
}
|
|
189
|
+
function representable(seconds, now) {
|
|
190
|
+
return Number.isNaN(new Date(now + seconds * 1e3).getTime()) ? void 0 : seconds;
|
|
191
|
+
}
|
|
192
|
+
function rateLimitDetails(headerValue, body, now = Date.now()) {
|
|
193
|
+
const fields = isRecord2(body) ? body : {};
|
|
194
|
+
const fromBody = typeof fields.retryAfterSeconds === "number" && Number.isFinite(fields.retryAfterSeconds) && fields.retryAfterSeconds >= 0 ? representable(Math.ceil(fields.retryAfterSeconds), now) : void 0;
|
|
195
|
+
const retryAfterSeconds = fromBody ?? parseRetryAfter(headerValue, now);
|
|
196
|
+
return {
|
|
197
|
+
status: 429,
|
|
198
|
+
...retryAfterSeconds !== void 0 ? { retryAfterSeconds, retryAt: new Date(now + retryAfterSeconds * 1e3).toISOString() } : {},
|
|
199
|
+
...typeof fields.scope === "string" ? { scope: fields.scope } : {},
|
|
200
|
+
...typeof fields.policyId === "string" ? { policyId: fields.policyId } : {}
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function isRateLimitedRpcError(error) {
|
|
204
|
+
return isRecord2(error) && error.code === 429 && isRecord2(error.data) && error.data.code === RATE_LIMITED;
|
|
205
|
+
}
|
|
206
|
+
function rateLimitMessage(where, details) {
|
|
207
|
+
return details.retryAfterSeconds === void 0 ? `${where} was rate limited (HTTP 429). Wait before trying again.` : `${where} was rate limited (HTTP 429). Retry after ${details.retryAfterSeconds} seconds (${details.retryAt}).`;
|
|
208
|
+
}
|
|
209
|
+
function isRecord2(value) {
|
|
210
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// src/core/harness/legacyRole.ts
|
|
214
|
+
var LEGACY_ROLE_HEADER = "X-AiNecto-Role";
|
|
215
|
+
var LEGACY_ROLE_ENV_VAR = "AINECTO_ROLE";
|
|
216
|
+
var LEGACY_ROLE_NOTICE = "AiNecto session roles were removed; access follows your AiNecto permissions. Re-run `ainecto init` to tidy this repository.";
|
|
217
|
+
function legacyRoleInput(flag, envVars = process.env) {
|
|
218
|
+
const value = flag ?? envVars[LEGACY_ROLE_ENV_VAR];
|
|
219
|
+
return value !== void 0 && value.trim() ? value.trim() : void 0;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// src/core/version.ts
|
|
223
|
+
var UNBUNDLED_VERSION = "dev";
|
|
224
|
+
var CLI_VERSION = true ? "0.12.1" : UNBUNDLED_VERSION;
|
|
225
|
+
var PACKAGE_NAME = "@ainecto/mcp";
|
|
226
|
+
var CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;
|
|
227
|
+
function bridgePackageSpec(version = CLI_VERSION) {
|
|
228
|
+
return version === UNBUNDLED_VERSION ? PACKAGE_NAME : `${PACKAGE_NAME}@${version}`;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// src/core/mcp/rpcClient.ts
|
|
232
|
+
var CHANNEL_HEADER = "X-AiNecto-Channel";
|
|
233
|
+
var AGENT_HEADER = "X-AiNecto-Agent";
|
|
234
|
+
var AGENT_NAME_HEADER = "X-AiNecto-Agent-Name";
|
|
235
|
+
var MAX_AGENT_NAME_LENGTH = 100;
|
|
236
|
+
var McpRpcError = class extends Error {
|
|
237
|
+
constructor(message, code, details) {
|
|
238
|
+
super(message);
|
|
239
|
+
this.code = code;
|
|
240
|
+
this.details = details;
|
|
241
|
+
}
|
|
242
|
+
code;
|
|
243
|
+
details;
|
|
244
|
+
};
|
|
245
|
+
var RateLimitedError = class extends McpRpcError {
|
|
246
|
+
constructor(message, rateLimit) {
|
|
247
|
+
super(message, RATE_LIMITED, rateLimit);
|
|
248
|
+
this.rateLimit = rateLimit;
|
|
249
|
+
this.name = "RateLimitedError";
|
|
250
|
+
}
|
|
251
|
+
rateLimit;
|
|
252
|
+
};
|
|
253
|
+
var McpRpcClient = class {
|
|
254
|
+
constructor(options) {
|
|
255
|
+
this.options = options;
|
|
256
|
+
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
257
|
+
}
|
|
258
|
+
options;
|
|
259
|
+
fetchImpl;
|
|
260
|
+
nextId = 1;
|
|
261
|
+
agent;
|
|
262
|
+
/**
|
|
263
|
+
* 브리지 전용 — 호스트 에이전트가 보낸 {@code initialize} 의 {@code clientInfo.name}(프로그램 고정 이름).
|
|
264
|
+
* 이후 모든 요청의 {@code X-AiNecto-Agent} 헤더로 싣는다. 직접 명령은 부르지 않는다(누가 셸을 돌렸는지 모른다).
|
|
265
|
+
*/
|
|
266
|
+
useAgent(clientName) {
|
|
267
|
+
this.agent = agentHeaderValue(clientName);
|
|
268
|
+
}
|
|
269
|
+
initialize() {
|
|
270
|
+
return this.request("initialize", {
|
|
271
|
+
protocolVersion: "2024-11-05",
|
|
272
|
+
capabilities: {},
|
|
273
|
+
clientInfo: { name: PACKAGE_NAME, version: CLI_VERSION }
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
async toolsList() {
|
|
277
|
+
const result = await this.request("tools/list", {});
|
|
278
|
+
if (!isRecord3(result) || !Array.isArray(result.tools)) {
|
|
279
|
+
throw new McpRpcError("tools/list response did not include a tools array.", "MCP_PROTOCOL_ERROR", result);
|
|
280
|
+
}
|
|
281
|
+
return result.tools;
|
|
282
|
+
}
|
|
283
|
+
async toolsCall(name, args) {
|
|
284
|
+
return this.request("tools/call", { name, arguments: args ?? {} });
|
|
285
|
+
}
|
|
286
|
+
async request(method, params) {
|
|
287
|
+
const response = await this.sendRaw({
|
|
288
|
+
jsonrpc: "2.0",
|
|
289
|
+
id: this.nextId++,
|
|
290
|
+
method,
|
|
291
|
+
params
|
|
292
|
+
});
|
|
293
|
+
if (!response) {
|
|
294
|
+
return void 0;
|
|
295
|
+
}
|
|
296
|
+
if ("error" in response) {
|
|
297
|
+
if (isRateLimitedRpcError(response.error)) {
|
|
298
|
+
const details = rateLimitDetails(void 0, response.error.data);
|
|
299
|
+
throw new RateLimitedError(rateLimitMessage(`MCP ${method}`, details), details);
|
|
300
|
+
}
|
|
301
|
+
throw new McpRpcError(response.error.message, "MCP_ERROR", response.error);
|
|
302
|
+
}
|
|
303
|
+
return response.result;
|
|
304
|
+
}
|
|
305
|
+
async sendRaw(request) {
|
|
306
|
+
const token = await this.options.tokenProvider?.getAccessToken();
|
|
307
|
+
let response = await this.postJson(request, token);
|
|
308
|
+
if (response.status === 401 && this.options.tokenProvider) {
|
|
309
|
+
const refreshed = await this.options.tokenProvider.refreshAfterUnauthorized();
|
|
310
|
+
if (refreshed) {
|
|
311
|
+
response = await this.postJson(request, refreshed);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (response.status === 204 || response.status === 202) {
|
|
315
|
+
return void 0;
|
|
316
|
+
}
|
|
317
|
+
const text = await response.text();
|
|
318
|
+
if (response.status === 429) {
|
|
319
|
+
return rateLimitedResponse(text, response.headers.get("retry-after"));
|
|
320
|
+
}
|
|
321
|
+
if (!response.ok) {
|
|
322
|
+
const serverError = parseServerError(text);
|
|
323
|
+
throw new McpRpcError(
|
|
324
|
+
`MCP HTTP request failed with HTTP ${response.status}${serverError ? ` \u2014 ${describeServerError(serverError)}` : "."}`,
|
|
325
|
+
"MCP_HTTP_ERROR",
|
|
326
|
+
{ ...httpErrorDetails(response.status, text, this.options.envVars ?? process.env), ...serverError ? { error: serverError.error } : {} }
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
if (!text.trim()) {
|
|
330
|
+
return void 0;
|
|
331
|
+
}
|
|
332
|
+
const parsed = JSON.parse(text);
|
|
333
|
+
if (!isJsonRpcResponse(parsed)) {
|
|
334
|
+
throw new McpRpcError("MCP response was not a JSON-RPC response.", "MCP_PROTOCOL_ERROR", parsed);
|
|
335
|
+
}
|
|
336
|
+
return parsed;
|
|
337
|
+
}
|
|
338
|
+
postJson(request, token) {
|
|
339
|
+
const headers = {
|
|
340
|
+
"content-type": "application/json",
|
|
341
|
+
accept: "application/json"
|
|
342
|
+
};
|
|
343
|
+
if (token) {
|
|
344
|
+
headers.authorization = `Bearer ${token}`;
|
|
345
|
+
}
|
|
346
|
+
const legacyRole = printableHeaderValue(this.options.legacyRole);
|
|
347
|
+
if (legacyRole) {
|
|
348
|
+
headers[LEGACY_ROLE_HEADER.toLowerCase()] = legacyRole;
|
|
349
|
+
}
|
|
350
|
+
headers[CHANNEL_HEADER.toLowerCase()] = this.options.channel;
|
|
351
|
+
if (this.agent) {
|
|
352
|
+
headers[AGENT_HEADER.toLowerCase()] = this.agent;
|
|
353
|
+
}
|
|
354
|
+
if (this.options.agentName) {
|
|
355
|
+
headers[AGENT_NAME_HEADER.toLowerCase()] = this.options.agentName;
|
|
356
|
+
}
|
|
357
|
+
return this.fetchImpl(this.options.endpoint, {
|
|
358
|
+
method: "POST",
|
|
359
|
+
headers,
|
|
360
|
+
body: JSON.stringify(request)
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
};
|
|
364
|
+
function rateLimitedResponse(text, retryAfter) {
|
|
365
|
+
let parsed;
|
|
366
|
+
try {
|
|
367
|
+
parsed = JSON.parse(text);
|
|
368
|
+
} catch {
|
|
369
|
+
parsed = void 0;
|
|
370
|
+
}
|
|
371
|
+
if (isJsonRpcResponse(parsed) && "error" in parsed) {
|
|
372
|
+
const data = isRecord3(parsed.error.data) ? parsed.error.data : {};
|
|
373
|
+
const seconds = parseRetryAfter(retryAfter);
|
|
374
|
+
if (data.retryAfterSeconds === void 0 && seconds !== void 0) {
|
|
375
|
+
parsed.error.data = { ...data, retryAfterSeconds: seconds };
|
|
376
|
+
}
|
|
377
|
+
return parsed;
|
|
378
|
+
}
|
|
379
|
+
const details = rateLimitDetails(retryAfter, parsed);
|
|
380
|
+
throw new RateLimitedError(rateLimitMessage("MCP HTTP request", details), details);
|
|
381
|
+
}
|
|
382
|
+
function isToolError(result) {
|
|
383
|
+
return isRecord3(result) && result.isError === true;
|
|
384
|
+
}
|
|
385
|
+
function agentHeaderValue(clientName) {
|
|
386
|
+
return printableHeaderValue(clientName);
|
|
387
|
+
}
|
|
388
|
+
function printableHeaderValue(value) {
|
|
389
|
+
if (typeof value !== "string") {
|
|
390
|
+
return void 0;
|
|
391
|
+
}
|
|
392
|
+
const printable = value.replace(/[^\x20-\x7E]/g, "").trim().slice(0, MAX_AGENT_NAME_LENGTH).trim();
|
|
393
|
+
return printable || void 0;
|
|
394
|
+
}
|
|
395
|
+
function parseServerError(body) {
|
|
396
|
+
let parsed;
|
|
397
|
+
try {
|
|
398
|
+
parsed = JSON.parse(body);
|
|
399
|
+
} catch {
|
|
400
|
+
return void 0;
|
|
401
|
+
}
|
|
402
|
+
if (!isRecord3(parsed) || typeof parsed.error !== "string" || !parsed.error.trim()) {
|
|
403
|
+
return void 0;
|
|
404
|
+
}
|
|
405
|
+
return {
|
|
406
|
+
error: parsed.error.trim(),
|
|
407
|
+
description: typeof parsed.error_description === "string" && parsed.error_description.trim() ? parsed.error_description : void 0
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function describeServerError(serverError) {
|
|
411
|
+
const line = serverError.description ? `${serverError.error}: ${serverError.description}` : serverError.error;
|
|
412
|
+
return line.replace(/\s+/g, " ").trim();
|
|
413
|
+
}
|
|
414
|
+
function isJsonRpcResponse(value) {
|
|
415
|
+
return isRecord3(value) && value.jsonrpc === "2.0" && ("result" in value || "error" in value);
|
|
416
|
+
}
|
|
417
|
+
function isRecord3(value) {
|
|
418
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
419
|
+
}
|
|
420
|
+
function httpErrorDetails(status, body, envVars) {
|
|
421
|
+
const details = { status };
|
|
422
|
+
if (envVars.AINECTO_CLI_DEBUG === "1" || envVars.AINECTO_DEBUG === "1") {
|
|
423
|
+
details.body = safeBodyPreview(body);
|
|
424
|
+
}
|
|
425
|
+
return details;
|
|
426
|
+
}
|
|
427
|
+
function safeBodyPreview(body) {
|
|
428
|
+
return body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
429
|
+
}
|
|
430
|
+
|
|
178
431
|
// src/core/auth/oauth.ts
|
|
179
432
|
var DEFAULT_LOGIN_TIMEOUT_MS = 3e5;
|
|
180
433
|
var OAuthLoginError = class extends Error {
|
|
@@ -388,6 +641,10 @@ async function exchangeToken(tokenEndpoint, params, fetchImpl) {
|
|
|
388
641
|
headers: { "content-type": "application/x-www-form-urlencoded" },
|
|
389
642
|
body: new URLSearchParams(params)
|
|
390
643
|
});
|
|
644
|
+
if (response.status === 429) {
|
|
645
|
+
const details = rateLimitDetails(response.headers.get("retry-after"), await readJsonObject(response));
|
|
646
|
+
throw new RateLimitedError(rateLimitMessage("OAuth token request", details), details);
|
|
647
|
+
}
|
|
391
648
|
if (!response.ok) {
|
|
392
649
|
const oauthError = await readOAuthErrorCode(response);
|
|
393
650
|
if (params.grant_type === "refresh_token" && oauthError !== void 0 && REAUTH_REQUIRED_ERRORS.has(oauthError)) {
|
|
@@ -397,6 +654,13 @@ async function exchangeToken(tokenEndpoint, params, fetchImpl) {
|
|
|
397
654
|
}
|
|
398
655
|
return response.json();
|
|
399
656
|
}
|
|
657
|
+
async function readJsonObject(response) {
|
|
658
|
+
try {
|
|
659
|
+
return await response.json();
|
|
660
|
+
} catch {
|
|
661
|
+
return void 0;
|
|
662
|
+
}
|
|
663
|
+
}
|
|
400
664
|
async function readOAuthErrorCode(response) {
|
|
401
665
|
try {
|
|
402
666
|
const body = await response.json();
|
|
@@ -612,191 +876,6 @@ async function readAllStdin(stdin) {
|
|
|
612
876
|
return text;
|
|
613
877
|
}
|
|
614
878
|
|
|
615
|
-
// src/core/harness/legacyRole.ts
|
|
616
|
-
var LEGACY_ROLE_HEADER = "X-AiNecto-Role";
|
|
617
|
-
var LEGACY_ROLE_ENV_VAR = "AINECTO_ROLE";
|
|
618
|
-
var LEGACY_ROLE_NOTICE = "AiNecto session roles were removed; access follows your AiNecto permissions. Re-run `ainecto init` to tidy this repository.";
|
|
619
|
-
function legacyRoleInput(flag, envVars = process.env) {
|
|
620
|
-
const value = flag ?? envVars[LEGACY_ROLE_ENV_VAR];
|
|
621
|
-
return value !== void 0 && value.trim() ? value.trim() : void 0;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
// src/core/version.ts
|
|
625
|
-
var UNBUNDLED_VERSION = "dev";
|
|
626
|
-
var CLI_VERSION = true ? "0.12.0" : UNBUNDLED_VERSION;
|
|
627
|
-
var PACKAGE_NAME = "@ainecto/mcp";
|
|
628
|
-
var CLI_USER_AGENT = `${PACKAGE_NAME} ${CLI_VERSION}`;
|
|
629
|
-
function bridgePackageSpec(version = CLI_VERSION) {
|
|
630
|
-
return version === UNBUNDLED_VERSION ? PACKAGE_NAME : `${PACKAGE_NAME}@${version}`;
|
|
631
|
-
}
|
|
632
|
-
|
|
633
|
-
// src/core/mcp/rpcClient.ts
|
|
634
|
-
var CHANNEL_HEADER = "X-AiNecto-Channel";
|
|
635
|
-
var AGENT_HEADER = "X-AiNecto-Agent";
|
|
636
|
-
var AGENT_NAME_HEADER = "X-AiNecto-Agent-Name";
|
|
637
|
-
var MAX_AGENT_NAME_LENGTH = 100;
|
|
638
|
-
var McpRpcError = class extends Error {
|
|
639
|
-
constructor(message, code, details) {
|
|
640
|
-
super(message);
|
|
641
|
-
this.code = code;
|
|
642
|
-
this.details = details;
|
|
643
|
-
}
|
|
644
|
-
code;
|
|
645
|
-
details;
|
|
646
|
-
};
|
|
647
|
-
var McpRpcClient = class {
|
|
648
|
-
constructor(options) {
|
|
649
|
-
this.options = options;
|
|
650
|
-
this.fetchImpl = options.fetchImpl ?? fetch;
|
|
651
|
-
}
|
|
652
|
-
options;
|
|
653
|
-
fetchImpl;
|
|
654
|
-
nextId = 1;
|
|
655
|
-
agent;
|
|
656
|
-
/**
|
|
657
|
-
* 브리지 전용 — 호스트 에이전트가 보낸 {@code initialize} 의 {@code clientInfo.name}(프로그램 고정 이름).
|
|
658
|
-
* 이후 모든 요청의 {@code X-AiNecto-Agent} 헤더로 싣는다. 직접 명령은 부르지 않는다(누가 셸을 돌렸는지 모른다).
|
|
659
|
-
*/
|
|
660
|
-
useAgent(clientName) {
|
|
661
|
-
this.agent = agentHeaderValue(clientName);
|
|
662
|
-
}
|
|
663
|
-
initialize() {
|
|
664
|
-
return this.request("initialize", {
|
|
665
|
-
protocolVersion: "2024-11-05",
|
|
666
|
-
capabilities: {},
|
|
667
|
-
clientInfo: { name: PACKAGE_NAME, version: CLI_VERSION }
|
|
668
|
-
});
|
|
669
|
-
}
|
|
670
|
-
async toolsList() {
|
|
671
|
-
const result = await this.request("tools/list", {});
|
|
672
|
-
if (!isRecord2(result) || !Array.isArray(result.tools)) {
|
|
673
|
-
throw new McpRpcError("tools/list response did not include a tools array.", "MCP_PROTOCOL_ERROR", result);
|
|
674
|
-
}
|
|
675
|
-
return result.tools;
|
|
676
|
-
}
|
|
677
|
-
async toolsCall(name, args) {
|
|
678
|
-
return this.request("tools/call", { name, arguments: args ?? {} });
|
|
679
|
-
}
|
|
680
|
-
async request(method, params) {
|
|
681
|
-
const response = await this.sendRaw({
|
|
682
|
-
jsonrpc: "2.0",
|
|
683
|
-
id: this.nextId++,
|
|
684
|
-
method,
|
|
685
|
-
params
|
|
686
|
-
});
|
|
687
|
-
if (!response) {
|
|
688
|
-
return void 0;
|
|
689
|
-
}
|
|
690
|
-
if ("error" in response) {
|
|
691
|
-
throw new McpRpcError(response.error.message, "MCP_ERROR", response.error);
|
|
692
|
-
}
|
|
693
|
-
return response.result;
|
|
694
|
-
}
|
|
695
|
-
async sendRaw(request) {
|
|
696
|
-
const token = await this.options.tokenProvider?.getAccessToken();
|
|
697
|
-
let response = await this.postJson(request, token);
|
|
698
|
-
if (response.status === 401 && this.options.tokenProvider) {
|
|
699
|
-
const refreshed = await this.options.tokenProvider.refreshAfterUnauthorized();
|
|
700
|
-
if (refreshed) {
|
|
701
|
-
response = await this.postJson(request, refreshed);
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
if (response.status === 204 || response.status === 202) {
|
|
705
|
-
return void 0;
|
|
706
|
-
}
|
|
707
|
-
const text = await response.text();
|
|
708
|
-
if (!response.ok) {
|
|
709
|
-
const serverError = parseServerError(text);
|
|
710
|
-
throw new McpRpcError(
|
|
711
|
-
`MCP HTTP request failed with HTTP ${response.status}${serverError ? ` \u2014 ${describeServerError(serverError)}` : "."}`,
|
|
712
|
-
"MCP_HTTP_ERROR",
|
|
713
|
-
{ ...httpErrorDetails(response.status, text, this.options.envVars ?? process.env), ...serverError ? { error: serverError.error } : {} }
|
|
714
|
-
);
|
|
715
|
-
}
|
|
716
|
-
if (!text.trim()) {
|
|
717
|
-
return void 0;
|
|
718
|
-
}
|
|
719
|
-
const parsed = JSON.parse(text);
|
|
720
|
-
if (!isJsonRpcResponse(parsed)) {
|
|
721
|
-
throw new McpRpcError("MCP response was not a JSON-RPC response.", "MCP_PROTOCOL_ERROR", parsed);
|
|
722
|
-
}
|
|
723
|
-
return parsed;
|
|
724
|
-
}
|
|
725
|
-
postJson(request, token) {
|
|
726
|
-
const headers = {
|
|
727
|
-
"content-type": "application/json",
|
|
728
|
-
accept: "application/json"
|
|
729
|
-
};
|
|
730
|
-
if (token) {
|
|
731
|
-
headers.authorization = `Bearer ${token}`;
|
|
732
|
-
}
|
|
733
|
-
const legacyRole = printableHeaderValue(this.options.legacyRole);
|
|
734
|
-
if (legacyRole) {
|
|
735
|
-
headers[LEGACY_ROLE_HEADER.toLowerCase()] = legacyRole;
|
|
736
|
-
}
|
|
737
|
-
headers[CHANNEL_HEADER.toLowerCase()] = this.options.channel;
|
|
738
|
-
if (this.agent) {
|
|
739
|
-
headers[AGENT_HEADER.toLowerCase()] = this.agent;
|
|
740
|
-
}
|
|
741
|
-
if (this.options.agentName) {
|
|
742
|
-
headers[AGENT_NAME_HEADER.toLowerCase()] = this.options.agentName;
|
|
743
|
-
}
|
|
744
|
-
return this.fetchImpl(this.options.endpoint, {
|
|
745
|
-
method: "POST",
|
|
746
|
-
headers,
|
|
747
|
-
body: JSON.stringify(request)
|
|
748
|
-
});
|
|
749
|
-
}
|
|
750
|
-
};
|
|
751
|
-
function isToolError(result) {
|
|
752
|
-
return isRecord2(result) && result.isError === true;
|
|
753
|
-
}
|
|
754
|
-
function agentHeaderValue(clientName) {
|
|
755
|
-
return printableHeaderValue(clientName);
|
|
756
|
-
}
|
|
757
|
-
function printableHeaderValue(value) {
|
|
758
|
-
if (typeof value !== "string") {
|
|
759
|
-
return void 0;
|
|
760
|
-
}
|
|
761
|
-
const printable = value.replace(/[^\x20-\x7E]/g, "").trim().slice(0, MAX_AGENT_NAME_LENGTH).trim();
|
|
762
|
-
return printable || void 0;
|
|
763
|
-
}
|
|
764
|
-
function parseServerError(body) {
|
|
765
|
-
let parsed;
|
|
766
|
-
try {
|
|
767
|
-
parsed = JSON.parse(body);
|
|
768
|
-
} catch {
|
|
769
|
-
return void 0;
|
|
770
|
-
}
|
|
771
|
-
if (!isRecord2(parsed) || typeof parsed.error !== "string" || !parsed.error.trim()) {
|
|
772
|
-
return void 0;
|
|
773
|
-
}
|
|
774
|
-
return {
|
|
775
|
-
error: parsed.error.trim(),
|
|
776
|
-
description: typeof parsed.error_description === "string" && parsed.error_description.trim() ? parsed.error_description : void 0
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
function describeServerError(serverError) {
|
|
780
|
-
const line = serverError.description ? `${serverError.error}: ${serverError.description}` : serverError.error;
|
|
781
|
-
return line.replace(/\s+/g, " ").trim();
|
|
782
|
-
}
|
|
783
|
-
function isJsonRpcResponse(value) {
|
|
784
|
-
return isRecord2(value) && value.jsonrpc === "2.0" && ("result" in value || "error" in value);
|
|
785
|
-
}
|
|
786
|
-
function isRecord2(value) {
|
|
787
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
788
|
-
}
|
|
789
|
-
function httpErrorDetails(status, body, envVars) {
|
|
790
|
-
const details = { status };
|
|
791
|
-
if (envVars.AINECTO_CLI_DEBUG === "1" || envVars.AINECTO_DEBUG === "1") {
|
|
792
|
-
details.body = safeBodyPreview(body);
|
|
793
|
-
}
|
|
794
|
-
return details;
|
|
795
|
-
}
|
|
796
|
-
function safeBodyPreview(body) {
|
|
797
|
-
return body.length > 500 ? `${body.slice(0, 500)}...` : body;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
879
|
// src/core/output/render.ts
|
|
801
880
|
function renderSuccess(data, options = {}) {
|
|
802
881
|
if (options.json) {
|
|
@@ -1159,7 +1238,7 @@ function inferPayloadMode(strippedName, inputSchema) {
|
|
|
1159
1238
|
return "binary-upload";
|
|
1160
1239
|
}
|
|
1161
1240
|
const properties = inputSchema.properties;
|
|
1162
|
-
if (!
|
|
1241
|
+
if (!isRecord4(properties)) {
|
|
1163
1242
|
return "flags";
|
|
1164
1243
|
}
|
|
1165
1244
|
const values = Object.values(properties);
|
|
@@ -1188,7 +1267,7 @@ function stableStringify(value) {
|
|
|
1188
1267
|
if (Array.isArray(value)) {
|
|
1189
1268
|
return `[${value.map(stableStringify).join(",")}]`;
|
|
1190
1269
|
}
|
|
1191
|
-
if (
|
|
1270
|
+
if (isRecord4(value)) {
|
|
1192
1271
|
return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableStringify(value[key])}`).join(",")}}`;
|
|
1193
1272
|
}
|
|
1194
1273
|
return JSON.stringify(value);
|
|
@@ -1197,7 +1276,7 @@ function normalizeSchema(schema) {
|
|
|
1197
1276
|
return JSON.parse(stableStringify(schema));
|
|
1198
1277
|
}
|
|
1199
1278
|
function isScalarSchema(value) {
|
|
1200
|
-
if (!
|
|
1279
|
+
if (!isRecord4(value)) {
|
|
1201
1280
|
return false;
|
|
1202
1281
|
}
|
|
1203
1282
|
const type = value.type;
|
|
@@ -1220,7 +1299,7 @@ function isVerb(value) {
|
|
|
1220
1299
|
function snakeToKebab(value) {
|
|
1221
1300
|
return value.replaceAll("_", "-");
|
|
1222
1301
|
}
|
|
1223
|
-
function
|
|
1302
|
+
function isRecord4(value) {
|
|
1224
1303
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1225
1304
|
}
|
|
1226
1305
|
|
|
@@ -1261,15 +1340,16 @@ async function runStdioBridge(options) {
|
|
|
1261
1340
|
`);
|
|
1262
1341
|
continue;
|
|
1263
1342
|
}
|
|
1264
|
-
writeLine(options.output, failure(request.id, INTERNAL_ERROR, error));
|
|
1343
|
+
writeLine(options.output, failure(request.id, error instanceof RateLimitedError ? RATE_LIMITED_CODE : INTERNAL_ERROR, error));
|
|
1265
1344
|
}
|
|
1266
1345
|
}
|
|
1267
1346
|
}
|
|
1268
1347
|
var PARSE_ERROR = -32700;
|
|
1269
1348
|
var INVALID_REQUEST = -32600;
|
|
1270
1349
|
var INTERNAL_ERROR = -32603;
|
|
1350
|
+
var RATE_LIMITED_CODE = 429;
|
|
1271
1351
|
function failure(id, code, error) {
|
|
1272
|
-
const data = error instanceof McpRpcError ? { code: error.code, ...
|
|
1352
|
+
const data = error instanceof McpRpcError ? { code: error.code, ...isRecord5(error.details) ? error.details : {} } : void 0;
|
|
1273
1353
|
return {
|
|
1274
1354
|
jsonrpc: "2.0",
|
|
1275
1355
|
id,
|
|
@@ -1285,7 +1365,7 @@ function writeLine(output, message) {
|
|
|
1285
1365
|
}
|
|
1286
1366
|
function parseJsonRpcRequest(line) {
|
|
1287
1367
|
const parsed = JSON.parse(line);
|
|
1288
|
-
if (!
|
|
1368
|
+
if (!isRecord5(parsed) || parsed.jsonrpc !== "2.0" || typeof parsed.method !== "string") {
|
|
1289
1369
|
throw new Error("Invalid JSON-RPC request.");
|
|
1290
1370
|
}
|
|
1291
1371
|
return {
|
|
@@ -1296,9 +1376,9 @@ function parseJsonRpcRequest(line) {
|
|
|
1296
1376
|
};
|
|
1297
1377
|
}
|
|
1298
1378
|
function clientInfoName(params) {
|
|
1299
|
-
return
|
|
1379
|
+
return isRecord5(params) && isRecord5(params.clientInfo) ? params.clientInfo.name : void 0;
|
|
1300
1380
|
}
|
|
1301
|
-
function
|
|
1381
|
+
function isRecord5(value) {
|
|
1302
1382
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1303
1383
|
}
|
|
1304
1384
|
|
|
@@ -1338,7 +1418,7 @@ function renderCodexConfig(entry) {
|
|
|
1338
1418
|
`;
|
|
1339
1419
|
}
|
|
1340
1420
|
function isOurEntry(entry, endpoint) {
|
|
1341
|
-
if (
|
|
1421
|
+
if (isRecord6(entry) && typeof entry.url === "string") {
|
|
1342
1422
|
const url = normalizedServerUrl(entry.url);
|
|
1343
1423
|
return url !== void 0 && (url === endpoint || isDefaultEndpoint(url));
|
|
1344
1424
|
}
|
|
@@ -1365,10 +1445,10 @@ function buildServerEntry(input = {}) {
|
|
|
1365
1445
|
return { command: "npx", args };
|
|
1366
1446
|
}
|
|
1367
1447
|
function entryCarriesRole(entry) {
|
|
1368
|
-
return
|
|
1448
|
+
return isRecord6(entry) && Array.isArray(entry.args) && entry.args.includes("--role");
|
|
1369
1449
|
}
|
|
1370
1450
|
function pinnedBridgeVersion(entry) {
|
|
1371
|
-
if (!
|
|
1451
|
+
if (!isRecord6(entry) || !Array.isArray(entry.args)) {
|
|
1372
1452
|
return void 0;
|
|
1373
1453
|
}
|
|
1374
1454
|
const pinned = entry.args.find((arg) => typeof arg === "string" && arg.startsWith(`${PACKAGE_NAME}@`));
|
|
@@ -1381,13 +1461,13 @@ function readServerEntry(existing) {
|
|
|
1381
1461
|
try {
|
|
1382
1462
|
const parsed = JSON.parse(existing);
|
|
1383
1463
|
const servers = parsed?.mcpServers;
|
|
1384
|
-
return
|
|
1464
|
+
return isRecord6(servers) ? servers[SERVER_NAME] : void 0;
|
|
1385
1465
|
} catch {
|
|
1386
1466
|
return void 0;
|
|
1387
1467
|
}
|
|
1388
1468
|
}
|
|
1389
1469
|
function isOurBridge(entry) {
|
|
1390
|
-
if (!
|
|
1470
|
+
if (!isRecord6(entry)) {
|
|
1391
1471
|
return false;
|
|
1392
1472
|
}
|
|
1393
1473
|
const { command, args } = entry;
|
|
@@ -1431,7 +1511,7 @@ function readServers(root) {
|
|
|
1431
1511
|
if (servers === void 0) {
|
|
1432
1512
|
return {};
|
|
1433
1513
|
}
|
|
1434
|
-
if (!
|
|
1514
|
+
if (!isRecord6(servers)) {
|
|
1435
1515
|
throw new Error(
|
|
1436
1516
|
'Existing MCP config has an "mcpServers" value that is not an object, so it was left untouched. Fix it by hand and run init again.'
|
|
1437
1517
|
);
|
|
@@ -1450,7 +1530,7 @@ function parseConfigObject(existing) {
|
|
|
1450
1530
|
`Existing MCP config is not valid JSON, so it was left untouched. Fix it by hand and run init again. (${error instanceof Error ? error.message : String(error)})`
|
|
1451
1531
|
);
|
|
1452
1532
|
}
|
|
1453
|
-
if (!
|
|
1533
|
+
if (!isRecord6(parsed)) {
|
|
1454
1534
|
throw new Error("Existing MCP config must be a JSON object; it was left untouched.");
|
|
1455
1535
|
}
|
|
1456
1536
|
return parsed;
|
|
@@ -1459,7 +1539,7 @@ function stringify(value) {
|
|
|
1459
1539
|
return `${JSON.stringify(value, null, 2)}
|
|
1460
1540
|
`;
|
|
1461
1541
|
}
|
|
1462
|
-
function
|
|
1542
|
+
function isRecord6(value) {
|
|
1463
1543
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1464
1544
|
}
|
|
1465
1545
|
|
|
@@ -1977,7 +2057,7 @@ var enrichments = [
|
|
|
1977
2057
|
// src/core/output/table.ts
|
|
1978
2058
|
function renderTable(data, options = {}) {
|
|
1979
2059
|
const rows = extractRows(data);
|
|
1980
|
-
if (!rows.length || !rows.every(
|
|
2060
|
+
if (!rows.length || !rows.every(isRecord7)) {
|
|
1981
2061
|
return void 0;
|
|
1982
2062
|
}
|
|
1983
2063
|
const columns = options.columns?.length ? options.columns : inferColumns(rows);
|
|
@@ -1994,7 +2074,7 @@ function extractRows(data) {
|
|
|
1994
2074
|
if (Array.isArray(data)) {
|
|
1995
2075
|
return data;
|
|
1996
2076
|
}
|
|
1997
|
-
if (
|
|
2077
|
+
if (isRecord7(data)) {
|
|
1998
2078
|
for (const key of ["items", "projects", "workspaces", "folders", "documents", "tasks", "attachments", "results", "data"]) {
|
|
1999
2079
|
const value = data[key];
|
|
2000
2080
|
if (Array.isArray(value)) {
|
|
@@ -2022,7 +2102,7 @@ function formatCell(value) {
|
|
|
2022
2102
|
}
|
|
2023
2103
|
return String(value);
|
|
2024
2104
|
}
|
|
2025
|
-
function
|
|
2105
|
+
function isRecord7(value) {
|
|
2026
2106
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2027
2107
|
}
|
|
2028
2108
|
|
|
@@ -2075,7 +2155,7 @@ function payloadToArgs(value) {
|
|
|
2075
2155
|
if (value === void 0 || value === null) {
|
|
2076
2156
|
return {};
|
|
2077
2157
|
}
|
|
2078
|
-
if (!
|
|
2158
|
+
if (!isRecord8(value)) {
|
|
2079
2159
|
throw new CliCommandError("INVALID_PAYLOAD", "Generated command payload must be a JSON object.", { value });
|
|
2080
2160
|
}
|
|
2081
2161
|
return { ...value };
|
|
@@ -2179,12 +2259,12 @@ function coerceFlagValue(value, schema, key) {
|
|
|
2179
2259
|
}
|
|
2180
2260
|
function getSchemaProperties(schema) {
|
|
2181
2261
|
const properties = schema.properties;
|
|
2182
|
-
if (!
|
|
2262
|
+
if (!isRecord8(properties)) {
|
|
2183
2263
|
return {};
|
|
2184
2264
|
}
|
|
2185
2265
|
const result = {};
|
|
2186
2266
|
for (const [key, value] of Object.entries(properties)) {
|
|
2187
|
-
if (
|
|
2267
|
+
if (isRecord8(value)) {
|
|
2188
2268
|
result[key] = value;
|
|
2189
2269
|
}
|
|
2190
2270
|
}
|
|
@@ -2199,7 +2279,7 @@ function requireValue(flag, value) {
|
|
|
2199
2279
|
}
|
|
2200
2280
|
return value;
|
|
2201
2281
|
}
|
|
2202
|
-
function
|
|
2282
|
+
function isRecord8(value) {
|
|
2203
2283
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2204
2284
|
}
|
|
2205
2285
|
|
|
@@ -2330,10 +2410,10 @@ import { basename as basename2, extname } from "path";
|
|
|
2330
2410
|
|
|
2331
2411
|
// src/core/mcp/toolPayload.ts
|
|
2332
2412
|
function unwrapMcpToolPayload(value) {
|
|
2333
|
-
if (!
|
|
2413
|
+
if (!isRecord9(value) || !Array.isArray(value.content)) {
|
|
2334
2414
|
return value;
|
|
2335
2415
|
}
|
|
2336
|
-
const text = value.content.map((item) =>
|
|
2416
|
+
const text = value.content.map((item) => isRecord9(item) && typeof item.text === "string" ? item.text : "").find((itemText) => itemText.trim().startsWith("{"));
|
|
2337
2417
|
if (!text) {
|
|
2338
2418
|
return value;
|
|
2339
2419
|
}
|
|
@@ -2344,23 +2424,23 @@ function unwrapMcpToolPayload(value) {
|
|
|
2344
2424
|
}
|
|
2345
2425
|
}
|
|
2346
2426
|
function toolErrorText(value) {
|
|
2347
|
-
if (!
|
|
2427
|
+
if (!isRecord9(value) || !Array.isArray(value.content)) {
|
|
2348
2428
|
return "The tool returned an error.";
|
|
2349
2429
|
}
|
|
2350
|
-
const text = value.content.map((item) =>
|
|
2430
|
+
const text = value.content.map((item) => isRecord9(item) && typeof item.text === "string" ? item.text : "").filter(Boolean).join("\n");
|
|
2351
2431
|
return text || "The tool returned an error.";
|
|
2352
2432
|
}
|
|
2353
|
-
function
|
|
2433
|
+
function isRecord9(value) {
|
|
2354
2434
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2355
2435
|
}
|
|
2356
2436
|
function extractProjects(value) {
|
|
2357
|
-
const candidates = Array.isArray(value) ? value :
|
|
2437
|
+
const candidates = Array.isArray(value) ? value : isRecord9(value) ? ["projects", "items", "created", "data", "hits"].flatMap((key) => {
|
|
2358
2438
|
const nested = value[key];
|
|
2359
2439
|
return Array.isArray(nested) ? nested : [];
|
|
2360
2440
|
}) : [];
|
|
2361
2441
|
const projects = [];
|
|
2362
2442
|
for (const candidate of candidates) {
|
|
2363
|
-
if (
|
|
2443
|
+
if (isRecord9(candidate) && typeof candidate.uuid === "string") {
|
|
2364
2444
|
projects.push({
|
|
2365
2445
|
uuid: candidate.uuid,
|
|
2366
2446
|
name: typeof candidate.name === "string" ? candidate.name : candidate.uuid
|
|
@@ -2468,7 +2548,7 @@ async function requestUploadToken(client, metadata, context) {
|
|
|
2468
2548
|
requestArgs.ttlSeconds = context.ttlSeconds;
|
|
2469
2549
|
}
|
|
2470
2550
|
const response = unwrapMcpToolPayload(await client.toolsCall(context.requestToolName, requestArgs));
|
|
2471
|
-
if (!
|
|
2551
|
+
if (!isRecord10(response)) {
|
|
2472
2552
|
throw new AttachmentUploadError("UPLOAD_TOKEN_PROTOCOL_ERROR", "request_upload_token response was not an object.");
|
|
2473
2553
|
}
|
|
2474
2554
|
const token = response.token;
|
|
@@ -2489,8 +2569,9 @@ async function putAttachmentBytes(token, metadata, fetchImpl) {
|
|
|
2489
2569
|
const maxAttempts = 3;
|
|
2490
2570
|
let lastRetryableError;
|
|
2491
2571
|
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
|
|
2572
|
+
let response;
|
|
2492
2573
|
try {
|
|
2493
|
-
|
|
2574
|
+
response = await fetchImpl(token.uploadUrl, {
|
|
2494
2575
|
method: "PUT",
|
|
2495
2576
|
headers: {
|
|
2496
2577
|
authorization: `Bearer ${token.token}`,
|
|
@@ -2498,22 +2579,27 @@ async function putAttachmentBytes(token, metadata, fetchImpl) {
|
|
|
2498
2579
|
},
|
|
2499
2580
|
body: new Uint8Array(metadata.bytes)
|
|
2500
2581
|
});
|
|
2501
|
-
if (shouldRetryResponse(response.status) && attempt < maxAttempts) {
|
|
2502
|
-
continue;
|
|
2503
|
-
}
|
|
2504
|
-
if (!response.ok) {
|
|
2505
|
-
throw uploadHttpError(response.status);
|
|
2506
|
-
}
|
|
2507
|
-
return parsePutResponse(await response.text());
|
|
2508
2582
|
} catch (error) {
|
|
2509
|
-
if (error instanceof AttachmentUploadError) {
|
|
2510
|
-
throw error;
|
|
2511
|
-
}
|
|
2512
2583
|
lastRetryableError = error;
|
|
2513
|
-
|
|
2514
|
-
|
|
2515
|
-
|
|
2584
|
+
continue;
|
|
2585
|
+
}
|
|
2586
|
+
if (response.status === 429) {
|
|
2587
|
+
throw await uploadRateLimited(response);
|
|
2588
|
+
}
|
|
2589
|
+
if (shouldRetryResponse(response.status) && attempt < maxAttempts) {
|
|
2590
|
+
continue;
|
|
2591
|
+
}
|
|
2592
|
+
if (!response.ok) {
|
|
2593
|
+
throw uploadHttpError(response.status);
|
|
2594
|
+
}
|
|
2595
|
+
let text;
|
|
2596
|
+
try {
|
|
2597
|
+
text = await response.text();
|
|
2598
|
+
} catch (error) {
|
|
2599
|
+
lastRetryableError = error;
|
|
2600
|
+
continue;
|
|
2516
2601
|
}
|
|
2602
|
+
return parsePutResponse(text);
|
|
2517
2603
|
}
|
|
2518
2604
|
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_FAILED", "Attachment upload failed.", {
|
|
2519
2605
|
cause: lastRetryableError instanceof Error ? lastRetryableError.message : String(lastRetryableError)
|
|
@@ -2529,7 +2615,17 @@ function uploadHttpError(status) {
|
|
|
2529
2615
|
return new AttachmentUploadError("ATTACHMENT_UPLOAD_HTTP_ERROR", `Attachment upload failed with HTTP ${status}.`, { status });
|
|
2530
2616
|
}
|
|
2531
2617
|
function shouldRetryResponse(status) {
|
|
2532
|
-
return status
|
|
2618
|
+
return status >= 500;
|
|
2619
|
+
}
|
|
2620
|
+
async function uploadRateLimited(response) {
|
|
2621
|
+
let body;
|
|
2622
|
+
try {
|
|
2623
|
+
body = await response.json();
|
|
2624
|
+
} catch {
|
|
2625
|
+
body = void 0;
|
|
2626
|
+
}
|
|
2627
|
+
const details = rateLimitDetails(response.headers.get("retry-after"), body);
|
|
2628
|
+
return new AttachmentUploadError(RATE_LIMITED, rateLimitMessage("Attachment upload", details), details);
|
|
2533
2629
|
}
|
|
2534
2630
|
function parsePutResponse(text) {
|
|
2535
2631
|
let parsed;
|
|
@@ -2538,7 +2634,7 @@ function parsePutResponse(text) {
|
|
|
2538
2634
|
} catch {
|
|
2539
2635
|
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not valid JSON.");
|
|
2540
2636
|
}
|
|
2541
|
-
if (!
|
|
2637
|
+
if (!isRecord10(parsed)) {
|
|
2542
2638
|
throw new AttachmentUploadError("ATTACHMENT_UPLOAD_PROTOCOL_ERROR", "Attachment PUT response was not an object.");
|
|
2543
2639
|
}
|
|
2544
2640
|
if (typeof parsed.storageKey !== "string") {
|
|
@@ -2602,7 +2698,7 @@ var MIME_BY_EXTENSION = {
|
|
|
2602
2698
|
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
2603
2699
|
".zip": "application/zip"
|
|
2604
2700
|
};
|
|
2605
|
-
function
|
|
2701
|
+
function isRecord10(value) {
|
|
2606
2702
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2607
2703
|
}
|
|
2608
2704
|
|
|
@@ -2834,7 +2930,7 @@ async function agentTodosPoll(options) {
|
|
|
2834
2930
|
let after;
|
|
2835
2931
|
for (let page = 0; page < 20; page += 1) {
|
|
2836
2932
|
const result = await callTool(options.client, LIST_AGENT_TODOS, { state: ["OPEN"], ...scope, limit: 50, ...after ? { after } : {} });
|
|
2837
|
-
const pageItems = Array.isArray(result.items) ? result.items.filter(
|
|
2933
|
+
const pageItems = Array.isArray(result.items) ? result.items.filter(isRecord11) : [];
|
|
2838
2934
|
items.push(...pageItems);
|
|
2839
2935
|
const last = pageItems[pageItems.length - 1];
|
|
2840
2936
|
if (result.hasMore !== true || !last || typeof last.todoId !== "string") break;
|
|
@@ -2859,7 +2955,7 @@ async function callTool(client, name, args) {
|
|
|
2859
2955
|
throw new CliCommandError("TOOL_ERROR", toolErrorText(raw), { tool: name });
|
|
2860
2956
|
}
|
|
2861
2957
|
const payload = unwrapMcpToolPayload(raw);
|
|
2862
|
-
if (!
|
|
2958
|
+
if (!isRecord11(payload)) {
|
|
2863
2959
|
throw new CliCommandError("MCP_PROTOCOL_ERROR", `${name} did not return an object.`, { tool: name });
|
|
2864
2960
|
}
|
|
2865
2961
|
return payload;
|
|
@@ -2934,7 +3030,7 @@ async function readCursorFile(path) {
|
|
|
2934
3030
|
if (text === void 0 || !text.trim()) return void 0;
|
|
2935
3031
|
try {
|
|
2936
3032
|
const parsed = JSON.parse(text);
|
|
2937
|
-
if (parsed?.v !== 1 || !
|
|
3033
|
+
if (parsed?.v !== 1 || !isRecord11(parsed.filter)) throw new Error("shape");
|
|
2938
3034
|
return { ...parsed, filter: normalizeSavedFilter(parsed.filter) };
|
|
2939
3035
|
} catch {
|
|
2940
3036
|
throw new CliCommandError("INVALID_CURSOR_FILE", `${path} is not a cursor file written by ainecto notifications poll.`, { cursorFile: path });
|
|
@@ -2969,7 +3065,7 @@ async function writeJsonAtomic(path, value) {
|
|
|
2969
3065
|
}
|
|
2970
3066
|
await rename2(temp, path);
|
|
2971
3067
|
}
|
|
2972
|
-
function
|
|
3068
|
+
function isRecord11(value) {
|
|
2973
3069
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
2974
3070
|
}
|
|
2975
3071
|
|
|
@@ -3985,9 +4081,9 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
|
|
|
3985
4081
|
}
|
|
3986
4082
|
const applied = [];
|
|
3987
4083
|
try {
|
|
3988
|
-
const
|
|
3989
|
-
const recordWrites = plan.writes.filter((write) =>
|
|
3990
|
-
const recordDeletes = plan.deletes.filter((path) =>
|
|
4084
|
+
const isRecord13 = (path) => !recordFirst && path === HARNESS_RECORD_PATH;
|
|
4085
|
+
const recordWrites = plan.writes.filter((write) => isRecord13(write.path));
|
|
4086
|
+
const recordDeletes = plan.deletes.filter((path) => isRecord13(path));
|
|
3991
4087
|
const createdDirectories = [...plan.record?.createdDirectories ?? []];
|
|
3992
4088
|
const rememberDirectories = (directories) => {
|
|
3993
4089
|
for (const directory of directories) {
|
|
@@ -4029,8 +4125,8 @@ async function applyPlan(root, plan, dryRun, snapshot, recordFirst = true) {
|
|
|
4029
4125
|
};
|
|
4030
4126
|
try {
|
|
4031
4127
|
await apply(
|
|
4032
|
-
orderedWrites(plan.writes.filter((write) => !
|
|
4033
|
-
plan.deletes.filter((path) => !
|
|
4128
|
+
orderedWrites(plan.writes.filter((write) => !isRecord13(write.path)), recordFirst),
|
|
4129
|
+
plan.deletes.filter((path) => !isRecord13(path))
|
|
4034
4130
|
);
|
|
4035
4131
|
await recordDirectoriesOrExplain(false);
|
|
4036
4132
|
await apply(recordWrites, recordDeletes);
|
|
@@ -4101,7 +4197,7 @@ async function fileMode(absolutePath) {
|
|
|
4101
4197
|
try {
|
|
4102
4198
|
return (await stat3(absolutePath)).mode & 4095;
|
|
4103
4199
|
} catch (error) {
|
|
4104
|
-
if (
|
|
4200
|
+
if (isRecord12(error) && error.code === "ENOENT") {
|
|
4105
4201
|
return void 0;
|
|
4106
4202
|
}
|
|
4107
4203
|
throw error;
|
|
@@ -4144,7 +4240,7 @@ function parseInitArgs(argv) {
|
|
|
4144
4240
|
}
|
|
4145
4241
|
return args;
|
|
4146
4242
|
}
|
|
4147
|
-
function
|
|
4243
|
+
function isRecord12(value) {
|
|
4148
4244
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
4149
4245
|
}
|
|
4150
4246
|
|