@swmansion/argent 0.9.0 → 0.11.0
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 +53 -10
- package/assets/argent.tracecfg.pbtxt +133 -0
- package/assets/queries/README.md +93 -0
- package/assets/queries/cpu-hotspots.sql +106 -0
- package/assets/queries/function-callers.sql +62 -0
- package/assets/queries/hang-folds-batched.sql +88 -0
- package/assets/queries/hang-main-thread-samples.sql +32 -0
- package/assets/queries/hang-state-breakdown.sql +44 -0
- package/assets/queries/memory-rss.sql +25 -0
- package/assets/queries/thread-breakdown.sql +34 -0
- package/assets/queries/trace-bounds.sql +6 -0
- package/assets/queries/ui-hangs.sql +59 -0
- package/assets/trace-processor/LICENSE +235 -0
- package/assets/trace-processor/SHA256SUMS +4 -0
- package/assets/trace-processor/engine.mjs +42423 -0
- package/assets/trace-processor/engine_bundle.node.js +11130 -0
- package/assets/trace-processor/trace_processor.wasm +0 -0
- package/{dist → bin}/argent-android-devtools-0.1.0.apk +0 -0
- package/bin/argent-simulator-server.cjs +56 -0
- package/bin/{ax-service → darwin/ax-service} +0 -0
- package/bin/{simulator-server → darwin/simulator-server} +0 -0
- package/bin/linux/simulator-server +0 -0
- package/dist/cli-cmds.mjs +2540 -85
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +23 -2
- package/dist/cli.js.map +1 -1
- package/dist/installer.mjs +241 -107
- package/dist/mcp-server.mjs +440 -110
- package/dist/tool-server.cjs +4297 -1895
- package/dylibs/libArgentInjectionBootstrap.dylib +0 -0
- package/dylibs/libKeyboardPatch.dylib +0 -0
- package/dylibs/libNativeDevtoolsIos.dylib +0 -0
- package/package.json +3 -3
- package/skills/argent-android-emulator-setup/SKILL.md +1 -1
- package/skills/argent-native-profiler/SKILL.md +5 -5
- /package/{dist → assets}/Argent.tracetemplate +0 -0
- /package/{manifest.json → assets/manifest.json} +0 -0
package/dist/mcp-server.mjs
CHANGED
|
@@ -2980,7 +2980,7 @@ var require_compile = __commonJS({
|
|
|
2980
2980
|
const schOrFunc = root.refs[ref];
|
|
2981
2981
|
if (schOrFunc)
|
|
2982
2982
|
return schOrFunc;
|
|
2983
|
-
let _sch =
|
|
2983
|
+
let _sch = resolve2.call(this, root, ref);
|
|
2984
2984
|
if (_sch === void 0) {
|
|
2985
2985
|
const schema = (_a3 = root.localRefs) === null || _a3 === void 0 ? void 0 : _a3[ref];
|
|
2986
2986
|
const { schemaId } = this.opts;
|
|
@@ -3007,7 +3007,7 @@ var require_compile = __commonJS({
|
|
|
3007
3007
|
function sameSchemaEnv(s1, s2) {
|
|
3008
3008
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3009
3009
|
}
|
|
3010
|
-
function
|
|
3010
|
+
function resolve2(root, ref) {
|
|
3011
3011
|
let sch;
|
|
3012
3012
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3013
3013
|
ref = sch;
|
|
@@ -3225,8 +3225,8 @@ var require_utils = __commonJS({
|
|
|
3225
3225
|
}
|
|
3226
3226
|
return ind;
|
|
3227
3227
|
}
|
|
3228
|
-
function removeDotSegments(
|
|
3229
|
-
let input =
|
|
3228
|
+
function removeDotSegments(path4) {
|
|
3229
|
+
let input = path4;
|
|
3230
3230
|
const output = [];
|
|
3231
3231
|
let nextSlash = -1;
|
|
3232
3232
|
let len = 0;
|
|
@@ -3478,8 +3478,8 @@ var require_schemes = __commonJS({
|
|
|
3478
3478
|
wsComponent.secure = void 0;
|
|
3479
3479
|
}
|
|
3480
3480
|
if (wsComponent.resourceName) {
|
|
3481
|
-
const [
|
|
3482
|
-
wsComponent.path =
|
|
3481
|
+
const [path4, query] = wsComponent.resourceName.split("?");
|
|
3482
|
+
wsComponent.path = path4 && path4 !== "/" ? path4 : void 0;
|
|
3483
3483
|
wsComponent.query = query;
|
|
3484
3484
|
wsComponent.resourceName = void 0;
|
|
3485
3485
|
}
|
|
@@ -3638,7 +3638,7 @@ var require_fast_uri = __commonJS({
|
|
|
3638
3638
|
}
|
|
3639
3639
|
return uri;
|
|
3640
3640
|
}
|
|
3641
|
-
function
|
|
3641
|
+
function resolve2(baseURI, relativeURI, options) {
|
|
3642
3642
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3643
3643
|
const resolved = resolveComponent(parse3(baseURI, schemelessOptions), parse3(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3644
3644
|
schemelessOptions.skipEscape = true;
|
|
@@ -3896,7 +3896,7 @@ var require_fast_uri = __commonJS({
|
|
|
3896
3896
|
var fastUri = {
|
|
3897
3897
|
SCHEMES,
|
|
3898
3898
|
normalize,
|
|
3899
|
-
resolve,
|
|
3899
|
+
resolve: resolve2,
|
|
3900
3900
|
resolveComponent,
|
|
3901
3901
|
equal,
|
|
3902
3902
|
serialize,
|
|
@@ -6872,12 +6872,12 @@ var require_dist = __commonJS({
|
|
|
6872
6872
|
throw new Error(`Unknown format "${name}"`);
|
|
6873
6873
|
return f;
|
|
6874
6874
|
};
|
|
6875
|
-
function addFormats(ajv, list,
|
|
6875
|
+
function addFormats(ajv, list, fs3, exportName) {
|
|
6876
6876
|
var _a3;
|
|
6877
6877
|
var _b;
|
|
6878
6878
|
(_a3 = (_b = ajv.opts.code).formats) !== null && _a3 !== void 0 ? _a3 : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
6879
6879
|
for (const f of list)
|
|
6880
|
-
ajv.addFormat(f,
|
|
6880
|
+
ajv.addFormat(f, fs3[f]);
|
|
6881
6881
|
}
|
|
6882
6882
|
module.exports = exports = formatsPlugin;
|
|
6883
6883
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
@@ -6886,9 +6886,9 @@ var require_dist = __commonJS({
|
|
|
6886
6886
|
});
|
|
6887
6887
|
|
|
6888
6888
|
// ../argent-mcp/src/mcp-server.ts
|
|
6889
|
-
import { appendFile, mkdir as
|
|
6890
|
-
import { dirname } from "node:path";
|
|
6891
|
-
import { homedir as
|
|
6889
|
+
import { appendFile, mkdir as mkdir4 } from "node:fs/promises";
|
|
6890
|
+
import { dirname as dirname2 } from "node:path";
|
|
6891
|
+
import { homedir as homedir4 } from "node:os";
|
|
6892
6892
|
|
|
6893
6893
|
// ../../node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js
|
|
6894
6894
|
import process3 from "node:process";
|
|
@@ -7133,10 +7133,10 @@ function mergeDefs(...defs) {
|
|
|
7133
7133
|
function cloneDef(schema) {
|
|
7134
7134
|
return mergeDefs(schema._zod.def);
|
|
7135
7135
|
}
|
|
7136
|
-
function getElementAtPath(obj,
|
|
7137
|
-
if (!
|
|
7136
|
+
function getElementAtPath(obj, path4) {
|
|
7137
|
+
if (!path4)
|
|
7138
7138
|
return obj;
|
|
7139
|
-
return
|
|
7139
|
+
return path4.reduce((acc, key) => acc?.[key], obj);
|
|
7140
7140
|
}
|
|
7141
7141
|
function promiseAllObject(promisesObj) {
|
|
7142
7142
|
const keys = Object.keys(promisesObj);
|
|
@@ -7545,11 +7545,11 @@ function explicitlyAborted(x, startIndex = 0) {
|
|
|
7545
7545
|
}
|
|
7546
7546
|
return false;
|
|
7547
7547
|
}
|
|
7548
|
-
function prefixIssues(
|
|
7548
|
+
function prefixIssues(path4, issues) {
|
|
7549
7549
|
return issues.map((iss) => {
|
|
7550
7550
|
var _a3;
|
|
7551
7551
|
(_a3 = iss).path ?? (_a3.path = []);
|
|
7552
|
-
iss.path.unshift(
|
|
7552
|
+
iss.path.unshift(path4);
|
|
7553
7553
|
return iss;
|
|
7554
7554
|
});
|
|
7555
7555
|
}
|
|
@@ -7696,16 +7696,16 @@ function flattenError(error2, mapper = (issue2) => issue2.message) {
|
|
|
7696
7696
|
}
|
|
7697
7697
|
function formatError(error2, mapper = (issue2) => issue2.message) {
|
|
7698
7698
|
const fieldErrors = { _errors: [] };
|
|
7699
|
-
const processError = (error3,
|
|
7699
|
+
const processError = (error3, path4 = []) => {
|
|
7700
7700
|
for (const issue2 of error3.issues) {
|
|
7701
7701
|
if (issue2.code === "invalid_union" && issue2.errors.length) {
|
|
7702
|
-
issue2.errors.map((issues) => processError({ issues }, [...
|
|
7702
|
+
issue2.errors.map((issues) => processError({ issues }, [...path4, ...issue2.path]));
|
|
7703
7703
|
} else if (issue2.code === "invalid_key") {
|
|
7704
|
-
processError({ issues: issue2.issues }, [...
|
|
7704
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
7705
7705
|
} else if (issue2.code === "invalid_element") {
|
|
7706
|
-
processError({ issues: issue2.issues }, [...
|
|
7706
|
+
processError({ issues: issue2.issues }, [...path4, ...issue2.path]);
|
|
7707
7707
|
} else {
|
|
7708
|
-
const fullpath = [...
|
|
7708
|
+
const fullpath = [...path4, ...issue2.path];
|
|
7709
7709
|
if (fullpath.length === 0) {
|
|
7710
7710
|
fieldErrors._errors.push(mapper(issue2));
|
|
7711
7711
|
} else {
|
|
@@ -13697,12 +13697,12 @@ var StdioServerTransport = class {
|
|
|
13697
13697
|
this.onclose?.();
|
|
13698
13698
|
}
|
|
13699
13699
|
send(message) {
|
|
13700
|
-
return new Promise((
|
|
13700
|
+
return new Promise((resolve2) => {
|
|
13701
13701
|
const json = serializeMessage(message);
|
|
13702
13702
|
if (this._stdout.write(json)) {
|
|
13703
|
-
|
|
13703
|
+
resolve2();
|
|
13704
13704
|
} else {
|
|
13705
|
-
this._stdout.once("drain",
|
|
13705
|
+
this._stdout.once("drain", resolve2);
|
|
13706
13706
|
}
|
|
13707
13707
|
});
|
|
13708
13708
|
}
|
|
@@ -14300,7 +14300,7 @@ var Protocol = class {
|
|
|
14300
14300
|
return;
|
|
14301
14301
|
}
|
|
14302
14302
|
const pollInterval = task2.pollInterval ?? this._options?.defaultTaskPollInterval ?? 1e3;
|
|
14303
|
-
await new Promise((
|
|
14303
|
+
await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
|
|
14304
14304
|
options?.signal?.throwIfAborted();
|
|
14305
14305
|
}
|
|
14306
14306
|
} catch (error2) {
|
|
@@ -14317,7 +14317,7 @@ var Protocol = class {
|
|
|
14317
14317
|
*/
|
|
14318
14318
|
request(request, resultSchema, options) {
|
|
14319
14319
|
const { relatedRequestId, resumptionToken, onresumptiontoken, task, relatedTask } = options ?? {};
|
|
14320
|
-
return new Promise((
|
|
14320
|
+
return new Promise((resolve2, reject) => {
|
|
14321
14321
|
const earlyReject = (error2) => {
|
|
14322
14322
|
reject(error2);
|
|
14323
14323
|
};
|
|
@@ -14395,7 +14395,7 @@ var Protocol = class {
|
|
|
14395
14395
|
if (!parseResult.success) {
|
|
14396
14396
|
reject(parseResult.error);
|
|
14397
14397
|
} else {
|
|
14398
|
-
|
|
14398
|
+
resolve2(parseResult.data);
|
|
14399
14399
|
}
|
|
14400
14400
|
} catch (error2) {
|
|
14401
14401
|
reject(error2);
|
|
@@ -14611,21 +14611,21 @@ var Protocol = class {
|
|
|
14611
14611
|
* the error appropriately (e.g., by failing the task, logging, etc.). The Protocol layer
|
|
14612
14612
|
* simply propagates the error.
|
|
14613
14613
|
*/
|
|
14614
|
-
async _enqueueTaskMessage(taskId, message,
|
|
14614
|
+
async _enqueueTaskMessage(taskId, message, sessionId2) {
|
|
14615
14615
|
if (!this._taskStore || !this._taskMessageQueue) {
|
|
14616
14616
|
throw new Error("Cannot enqueue task message: taskStore and taskMessageQueue are not configured");
|
|
14617
14617
|
}
|
|
14618
14618
|
const maxQueueSize = this._options?.maxTaskQueueSize;
|
|
14619
|
-
await this._taskMessageQueue.enqueue(taskId, message,
|
|
14619
|
+
await this._taskMessageQueue.enqueue(taskId, message, sessionId2, maxQueueSize);
|
|
14620
14620
|
}
|
|
14621
14621
|
/**
|
|
14622
14622
|
* Clears the message queue for a task and rejects any pending request resolvers.
|
|
14623
14623
|
* @param taskId The task ID whose queue should be cleared
|
|
14624
14624
|
* @param sessionId Optional session ID for binding the operation to a specific session
|
|
14625
14625
|
*/
|
|
14626
|
-
async _clearTaskQueue(taskId,
|
|
14626
|
+
async _clearTaskQueue(taskId, sessionId2) {
|
|
14627
14627
|
if (this._taskMessageQueue) {
|
|
14628
|
-
const messages = await this._taskMessageQueue.dequeueAll(taskId,
|
|
14628
|
+
const messages = await this._taskMessageQueue.dequeueAll(taskId, sessionId2);
|
|
14629
14629
|
for (const message of messages) {
|
|
14630
14630
|
if (message.type === "request" && isJSONRPCRequest(message.message)) {
|
|
14631
14631
|
const requestId = message.message.id;
|
|
@@ -14656,19 +14656,19 @@ var Protocol = class {
|
|
|
14656
14656
|
}
|
|
14657
14657
|
} catch {
|
|
14658
14658
|
}
|
|
14659
|
-
return new Promise((
|
|
14659
|
+
return new Promise((resolve2, reject) => {
|
|
14660
14660
|
if (signal.aborted) {
|
|
14661
14661
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
14662
14662
|
return;
|
|
14663
14663
|
}
|
|
14664
|
-
const timeoutId = setTimeout(
|
|
14664
|
+
const timeoutId = setTimeout(resolve2, interval);
|
|
14665
14665
|
signal.addEventListener("abort", () => {
|
|
14666
14666
|
clearTimeout(timeoutId);
|
|
14667
14667
|
reject(new McpError(ErrorCode.InvalidRequest, "Request cancelled"));
|
|
14668
14668
|
}, { once: true });
|
|
14669
14669
|
});
|
|
14670
14670
|
}
|
|
14671
|
-
requestTaskStore(request,
|
|
14671
|
+
requestTaskStore(request, sessionId2) {
|
|
14672
14672
|
const taskStore = this._taskStore;
|
|
14673
14673
|
if (!taskStore) {
|
|
14674
14674
|
throw new Error("No task store configured");
|
|
@@ -14681,18 +14681,18 @@ var Protocol = class {
|
|
|
14681
14681
|
return await taskStore.createTask(taskParams, request.id, {
|
|
14682
14682
|
method: request.method,
|
|
14683
14683
|
params: request.params
|
|
14684
|
-
},
|
|
14684
|
+
}, sessionId2);
|
|
14685
14685
|
},
|
|
14686
14686
|
getTask: async (taskId) => {
|
|
14687
|
-
const task = await taskStore.getTask(taskId,
|
|
14687
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
14688
14688
|
if (!task) {
|
|
14689
14689
|
throw new McpError(ErrorCode.InvalidParams, "Failed to retrieve task: Task not found");
|
|
14690
14690
|
}
|
|
14691
14691
|
return task;
|
|
14692
14692
|
},
|
|
14693
14693
|
storeTaskResult: async (taskId, status, result) => {
|
|
14694
|
-
await taskStore.storeTaskResult(taskId, status, result,
|
|
14695
|
-
const task = await taskStore.getTask(taskId,
|
|
14694
|
+
await taskStore.storeTaskResult(taskId, status, result, sessionId2);
|
|
14695
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
14696
14696
|
if (task) {
|
|
14697
14697
|
const notification = TaskStatusNotificationSchema.parse({
|
|
14698
14698
|
method: "notifications/tasks/status",
|
|
@@ -14705,18 +14705,18 @@ var Protocol = class {
|
|
|
14705
14705
|
}
|
|
14706
14706
|
},
|
|
14707
14707
|
getTaskResult: (taskId) => {
|
|
14708
|
-
return taskStore.getTaskResult(taskId,
|
|
14708
|
+
return taskStore.getTaskResult(taskId, sessionId2);
|
|
14709
14709
|
},
|
|
14710
14710
|
updateTaskStatus: async (taskId, status, statusMessage) => {
|
|
14711
|
-
const task = await taskStore.getTask(taskId,
|
|
14711
|
+
const task = await taskStore.getTask(taskId, sessionId2);
|
|
14712
14712
|
if (!task) {
|
|
14713
14713
|
throw new McpError(ErrorCode.InvalidParams, `Task "${taskId}" not found - it may have been cleaned up`);
|
|
14714
14714
|
}
|
|
14715
14715
|
if (isTerminal(task.status)) {
|
|
14716
14716
|
throw new McpError(ErrorCode.InvalidParams, `Cannot update task "${taskId}" from terminal status "${task.status}" to "${status}". Terminal states (completed, failed, cancelled) cannot transition to other states.`);
|
|
14717
14717
|
}
|
|
14718
|
-
await taskStore.updateTaskStatus(taskId, status, statusMessage,
|
|
14719
|
-
const updatedTask = await taskStore.getTask(taskId,
|
|
14718
|
+
await taskStore.updateTaskStatus(taskId, status, statusMessage, sessionId2);
|
|
14719
|
+
const updatedTask = await taskStore.getTask(taskId, sessionId2);
|
|
14720
14720
|
if (updatedTask) {
|
|
14721
14721
|
const notification = TaskStatusNotificationSchema.parse({
|
|
14722
14722
|
method: "notifications/tasks/status",
|
|
@@ -14729,7 +14729,7 @@ var Protocol = class {
|
|
|
14729
14729
|
}
|
|
14730
14730
|
},
|
|
14731
14731
|
listTasks: (cursor) => {
|
|
14732
|
-
return taskStore.listTasks(cursor,
|
|
14732
|
+
return taskStore.listTasks(cursor, sessionId2);
|
|
14733
14733
|
}
|
|
14734
14734
|
};
|
|
14735
14735
|
}
|
|
@@ -15080,8 +15080,8 @@ var Server = class extends Protocol {
|
|
|
15080
15080
|
this._serverInfo = _serverInfo;
|
|
15081
15081
|
this._loggingLevels = /* @__PURE__ */ new Map();
|
|
15082
15082
|
this.LOG_LEVEL_SEVERITY = new Map(LoggingLevelSchema.options.map((level, index) => [level, index]));
|
|
15083
|
-
this.isMessageIgnored = (level,
|
|
15084
|
-
const currentLevel = this._loggingLevels.get(
|
|
15083
|
+
this.isMessageIgnored = (level, sessionId2) => {
|
|
15084
|
+
const currentLevel = this._loggingLevels.get(sessionId2);
|
|
15085
15085
|
return currentLevel ? this.LOG_LEVEL_SEVERITY.get(level) < this.LOG_LEVEL_SEVERITY.get(currentLevel) : false;
|
|
15086
15086
|
};
|
|
15087
15087
|
this._capabilities = options?.capabilities ?? {};
|
|
@@ -15424,9 +15424,9 @@ var Server = class extends Protocol {
|
|
|
15424
15424
|
* @param params
|
|
15425
15425
|
* @param sessionId optional for stateless and backward compatibility
|
|
15426
15426
|
*/
|
|
15427
|
-
async sendLoggingMessage(params,
|
|
15427
|
+
async sendLoggingMessage(params, sessionId2) {
|
|
15428
15428
|
if (this._capabilities.logging) {
|
|
15429
|
-
if (!this.isMessageIgnored(params.level,
|
|
15429
|
+
if (!this.isMessageIgnored(params.level, sessionId2)) {
|
|
15430
15430
|
return this.notification({ method: "notifications/message", params });
|
|
15431
15431
|
}
|
|
15432
15432
|
}
|
|
@@ -15457,20 +15457,33 @@ import * as path from "node:path";
|
|
|
15457
15457
|
import * as readline from "node:readline";
|
|
15458
15458
|
import { homedir } from "node:os";
|
|
15459
15459
|
import { spawn } from "node:child_process";
|
|
15460
|
-
import {
|
|
15460
|
+
import { randomBytes } from "node:crypto";
|
|
15461
|
+
import { mkdir, writeFile, readFile, unlink, rename, chmod } from "node:fs/promises";
|
|
15461
15462
|
var STATE_DIR = path.join(homedir(), ".argent");
|
|
15462
15463
|
var STATE_FILE = path.join(STATE_DIR, "tool-server.json");
|
|
15463
15464
|
var LOG_FILE = path.join(STATE_DIR, "tool-server.log");
|
|
15464
|
-
|
|
15465
|
-
|
|
15465
|
+
var AUTH_TOKEN_BYTES = 32;
|
|
15466
|
+
var AUTH_TOKEN_ENV = "ARGENT_AUTH_TOKEN";
|
|
15467
|
+
var AUTOSPAWN_IDLE_TIMEOUT_MINUTES = 30;
|
|
15468
|
+
function buildToolsServerEnv(paths, port, baseEnv = process.env, options = {}) {
|
|
15469
|
+
const env = {
|
|
15466
15470
|
...baseEnv,
|
|
15467
|
-
|
|
15471
|
+
ARGENT_PORT: String(port),
|
|
15468
15472
|
ARGENT_SIMULATOR_SERVER_DIR: paths.simulatorServerDir,
|
|
15469
15473
|
ARGENT_NATIVE_DEVTOOLS_DIR: paths.nativeDevtoolsDir
|
|
15470
15474
|
};
|
|
15475
|
+
if (options.host !== void 0) env.ARGENT_HOST = options.host;
|
|
15476
|
+
if (options.idleTimeoutMinutes !== void 0) {
|
|
15477
|
+
env.ARGENT_IDLE_TIMEOUT_MINUTES = String(options.idleTimeoutMinutes);
|
|
15478
|
+
}
|
|
15479
|
+
if (options.token) env[AUTH_TOKEN_ENV] = options.token;
|
|
15480
|
+
return env;
|
|
15481
|
+
}
|
|
15482
|
+
function generateToken() {
|
|
15483
|
+
return randomBytes(AUTH_TOKEN_BYTES).toString("hex");
|
|
15471
15484
|
}
|
|
15472
15485
|
function findFreePort() {
|
|
15473
|
-
return new Promise((
|
|
15486
|
+
return new Promise((resolve2, reject) => {
|
|
15474
15487
|
const srv = net.createServer();
|
|
15475
15488
|
srv.listen(0, "127.0.0.1", () => {
|
|
15476
15489
|
const addr = srv.address();
|
|
@@ -15481,7 +15494,7 @@ function findFreePort() {
|
|
|
15481
15494
|
const port = addr.port;
|
|
15482
15495
|
srv.close((err) => {
|
|
15483
15496
|
if (err) reject(err);
|
|
15484
|
-
else
|
|
15497
|
+
else resolve2(port);
|
|
15485
15498
|
});
|
|
15486
15499
|
});
|
|
15487
15500
|
srv.on("error", reject);
|
|
@@ -15495,12 +15508,25 @@ function isProcessAlive(pid) {
|
|
|
15495
15508
|
return false;
|
|
15496
15509
|
}
|
|
15497
15510
|
}
|
|
15498
|
-
|
|
15511
|
+
function healthCheckHost(host) {
|
|
15512
|
+
if (host === "0.0.0.0" || host === "") return "127.0.0.1";
|
|
15513
|
+
if (host === "::" || host === "::0") return "::1";
|
|
15514
|
+
return host;
|
|
15515
|
+
}
|
|
15516
|
+
function formatUrl(host, port) {
|
|
15517
|
+
const h = host.includes(":") && !host.startsWith("[") ? `[${host}]` : host;
|
|
15518
|
+
return `http://${h}:${port}`;
|
|
15519
|
+
}
|
|
15520
|
+
function authHeaders(token) {
|
|
15521
|
+
return token ? { Authorization: `Bearer ${token}` } : {};
|
|
15522
|
+
}
|
|
15523
|
+
async function isToolsServerHealthy(port, host = "127.0.0.1", timeoutMs = 2e3, token) {
|
|
15499
15524
|
const controller = new AbortController();
|
|
15500
|
-
const timer = setTimeout(() => controller.abort(),
|
|
15525
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
15501
15526
|
try {
|
|
15502
|
-
const res = await fetch(
|
|
15503
|
-
signal: controller.signal
|
|
15527
|
+
const res = await fetch(`${formatUrl(healthCheckHost(host), port)}/tools`, {
|
|
15528
|
+
signal: controller.signal,
|
|
15529
|
+
headers: authHeaders(token)
|
|
15504
15530
|
});
|
|
15505
15531
|
return res.ok;
|
|
15506
15532
|
} catch {
|
|
@@ -15509,8 +15535,8 @@ async function isHealthy(port) {
|
|
|
15509
15535
|
clearTimeout(timer);
|
|
15510
15536
|
}
|
|
15511
15537
|
}
|
|
15512
|
-
function spawnToolsServer(paths, port) {
|
|
15513
|
-
return new Promise((
|
|
15538
|
+
function spawnToolsServer(paths, port, options = {}) {
|
|
15539
|
+
return new Promise((resolve2, reject) => {
|
|
15514
15540
|
let logFd;
|
|
15515
15541
|
try {
|
|
15516
15542
|
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
@@ -15521,7 +15547,7 @@ function spawnToolsServer(paths, port) {
|
|
|
15521
15547
|
const child = spawn("node", [paths.bundlePath, "start"], {
|
|
15522
15548
|
detached: true,
|
|
15523
15549
|
stdio: ["ignore", "pipe", logFd],
|
|
15524
|
-
env: buildToolsServerEnv(paths, port)
|
|
15550
|
+
env: buildToolsServerEnv(paths, port, process.env, options)
|
|
15525
15551
|
});
|
|
15526
15552
|
child.unref();
|
|
15527
15553
|
const pid = child.pid;
|
|
@@ -15537,12 +15563,12 @@ function spawnToolsServer(paths, port) {
|
|
|
15537
15563
|
};
|
|
15538
15564
|
const rl = readline.createInterface({ input: child.stdout });
|
|
15539
15565
|
rl.on("line", (line) => {
|
|
15540
|
-
const match = line.match(/Tools server listening on http
|
|
15566
|
+
const match = line.match(/Tools server listening on http:\/\/.+:(\d+)/);
|
|
15541
15567
|
if (match) {
|
|
15542
15568
|
const actualPort = parseInt(match[1], 10);
|
|
15543
15569
|
rl.close();
|
|
15544
15570
|
child.stdout?.resume();
|
|
15545
|
-
settle(() =>
|
|
15571
|
+
settle(() => resolve2({ port: actualPort, pid }));
|
|
15546
15572
|
}
|
|
15547
15573
|
});
|
|
15548
15574
|
child.on("error", (err) => {
|
|
@@ -15560,7 +15586,7 @@ function spawnToolsServer(paths, port) {
|
|
|
15560
15586
|
rl.on("close", () => clearTimeout(timer));
|
|
15561
15587
|
});
|
|
15562
15588
|
}
|
|
15563
|
-
async function
|
|
15589
|
+
async function readToolsServerState() {
|
|
15564
15590
|
try {
|
|
15565
15591
|
const raw = await readFile(STATE_FILE, "utf8");
|
|
15566
15592
|
return JSON.parse(raw);
|
|
@@ -15568,42 +15594,249 @@ async function readState() {
|
|
|
15568
15594
|
return null;
|
|
15569
15595
|
}
|
|
15570
15596
|
}
|
|
15571
|
-
async function
|
|
15597
|
+
async function writeToolsServerState(state) {
|
|
15572
15598
|
await mkdir(STATE_DIR, { recursive: true });
|
|
15573
|
-
|
|
15599
|
+
const tmp = `${STATE_FILE}.${process.pid}.tmp`;
|
|
15600
|
+
try {
|
|
15601
|
+
await writeFile(tmp, JSON.stringify(state, null, 2) + "\n", {
|
|
15602
|
+
encoding: "utf8",
|
|
15603
|
+
mode: 384
|
|
15604
|
+
});
|
|
15605
|
+
await chmod(tmp, 384);
|
|
15606
|
+
await rename(tmp, STATE_FILE);
|
|
15607
|
+
} catch (err) {
|
|
15608
|
+
await unlink(tmp).catch(() => {
|
|
15609
|
+
});
|
|
15610
|
+
throw err;
|
|
15611
|
+
}
|
|
15574
15612
|
}
|
|
15575
|
-
async function
|
|
15613
|
+
async function clearToolsServerState() {
|
|
15576
15614
|
try {
|
|
15577
15615
|
await unlink(STATE_FILE);
|
|
15578
15616
|
} catch {
|
|
15579
15617
|
}
|
|
15580
15618
|
}
|
|
15619
|
+
var readState = readToolsServerState;
|
|
15620
|
+
var writeState = writeToolsServerState;
|
|
15621
|
+
var clearState = clearToolsServerState;
|
|
15581
15622
|
async function ensureToolsServer(paths) {
|
|
15582
15623
|
const state = await readState();
|
|
15583
15624
|
if (state) {
|
|
15584
15625
|
const alive = isProcessAlive(state.pid);
|
|
15585
15626
|
if (alive) {
|
|
15586
|
-
const
|
|
15627
|
+
const host = state.host ?? "127.0.0.1";
|
|
15628
|
+
const healthy = await isToolsServerHealthy(state.port, host, 2e3, state.token);
|
|
15587
15629
|
if (healthy) {
|
|
15588
|
-
return
|
|
15630
|
+
return {
|
|
15631
|
+
url: formatUrl(healthCheckHost(host), state.port),
|
|
15632
|
+
token: state.token ?? ""
|
|
15633
|
+
};
|
|
15589
15634
|
}
|
|
15590
15635
|
}
|
|
15591
15636
|
await clearState();
|
|
15592
15637
|
}
|
|
15638
|
+
const token = generateToken();
|
|
15593
15639
|
const port = await findFreePort();
|
|
15594
|
-
const { port: actualPort, pid } = await spawnToolsServer(paths, port
|
|
15640
|
+
const { port: actualPort, pid } = await spawnToolsServer(paths, port, {
|
|
15641
|
+
token,
|
|
15642
|
+
idleTimeoutMinutes: AUTOSPAWN_IDLE_TIMEOUT_MINUTES
|
|
15643
|
+
});
|
|
15595
15644
|
await writeState({
|
|
15596
15645
|
port: actualPort,
|
|
15597
15646
|
pid,
|
|
15598
15647
|
startedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15599
|
-
bundlePath: paths.bundlePath
|
|
15648
|
+
bundlePath: paths.bundlePath,
|
|
15649
|
+
host: "127.0.0.1",
|
|
15650
|
+
token
|
|
15600
15651
|
});
|
|
15601
|
-
return
|
|
15652
|
+
return { url: formatUrl("127.0.0.1", actualPort), token };
|
|
15653
|
+
}
|
|
15654
|
+
|
|
15655
|
+
// ../argent-tools-client/src/link-config.ts
|
|
15656
|
+
import * as path2 from "node:path";
|
|
15657
|
+
import { homedir as homedir2 } from "node:os";
|
|
15658
|
+
import { mkdir as mkdir2, writeFile as writeFile2, readFile as readFile2, unlink as unlink2, chmod as chmod2 } from "node:fs/promises";
|
|
15659
|
+
var LINK_DIR = path2.join(homedir2(), ".argent");
|
|
15660
|
+
var LINK_FILE = path2.join(LINK_DIR, "link.json");
|
|
15661
|
+
async function readLinkConfig() {
|
|
15662
|
+
try {
|
|
15663
|
+
const raw = await readFile2(LINK_FILE, "utf8");
|
|
15664
|
+
const parsed = JSON.parse(raw);
|
|
15665
|
+
if (!parsed || typeof parsed.url !== "string" || typeof parsed.host !== "string" || typeof parsed.port !== "number" || typeof parsed.createdAt !== "string") {
|
|
15666
|
+
return null;
|
|
15667
|
+
}
|
|
15668
|
+
return {
|
|
15669
|
+
url: parsed.url,
|
|
15670
|
+
host: parsed.host,
|
|
15671
|
+
port: parsed.port,
|
|
15672
|
+
createdAt: parsed.createdAt,
|
|
15673
|
+
...typeof parsed.token === "string" ? { token: parsed.token } : {}
|
|
15674
|
+
};
|
|
15675
|
+
} catch {
|
|
15676
|
+
return null;
|
|
15677
|
+
}
|
|
15678
|
+
}
|
|
15679
|
+
async function getResolvedToolsUrl() {
|
|
15680
|
+
const envUrl = process.env.ARGENT_TOOLS_URL;
|
|
15681
|
+
if (envUrl) {
|
|
15682
|
+
const link2 = await readLinkConfig();
|
|
15683
|
+
const envToken = process.env[AUTH_TOKEN_ENV];
|
|
15684
|
+
return {
|
|
15685
|
+
url: envUrl,
|
|
15686
|
+
source: "env",
|
|
15687
|
+
...envToken ? { token: envToken } : {},
|
|
15688
|
+
...link2 ? { shadowedLink: link2 } : {}
|
|
15689
|
+
};
|
|
15690
|
+
}
|
|
15691
|
+
const link = await readLinkConfig();
|
|
15692
|
+
if (link) {
|
|
15693
|
+
return { url: link.url, source: "link", ...link.token ? { token: link.token } : {} };
|
|
15694
|
+
}
|
|
15695
|
+
return { url: null, source: "none" };
|
|
15696
|
+
}
|
|
15697
|
+
async function isRemoteRouted() {
|
|
15698
|
+
const { url } = await getResolvedToolsUrl();
|
|
15699
|
+
return url !== null;
|
|
15700
|
+
}
|
|
15701
|
+
|
|
15702
|
+
// ../argent-tools-client/src/artifacts.ts
|
|
15703
|
+
import { mkdir as mkdir3, readFile as readFile3, rm, stat, writeFile as writeFile3 } from "node:fs/promises";
|
|
15704
|
+
import { tmpdir } from "node:os";
|
|
15705
|
+
import { basename, join as join3 } from "node:path";
|
|
15706
|
+
import { createHash } from "node:crypto";
|
|
15707
|
+
import { execFile } from "node:child_process";
|
|
15708
|
+
import { promisify } from "node:util";
|
|
15709
|
+
var execFileAsync = promisify(execFile);
|
|
15710
|
+
var ARTIFACT_MARKER = "__argentArtifact";
|
|
15711
|
+
function isArtifactHandle(value) {
|
|
15712
|
+
return !!value && typeof value === "object" && value[ARTIFACT_MARKER] === true && typeof value.id === "string" && typeof value.filename === "string";
|
|
15713
|
+
}
|
|
15714
|
+
var SESSION_ID = null;
|
|
15715
|
+
function sessionId() {
|
|
15716
|
+
if (!SESSION_ID) {
|
|
15717
|
+
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:T]/g, (m) => m === "T" ? "-" : "").slice(0, 15);
|
|
15718
|
+
SESSION_ID = `${stamp}-${process.pid}`;
|
|
15719
|
+
}
|
|
15720
|
+
return SESSION_ID;
|
|
15721
|
+
}
|
|
15722
|
+
function sanitizeSegment(segment) {
|
|
15723
|
+
return segment.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
15724
|
+
}
|
|
15725
|
+
function projectSlug() {
|
|
15726
|
+
const cwd = process.cwd();
|
|
15727
|
+
const hash = createHash("sha1").update(cwd).digest("hex").slice(0, 6);
|
|
15728
|
+
const name = sanitizeSegment(basename(cwd)) || "root";
|
|
15729
|
+
return `${name}-${hash}`;
|
|
15730
|
+
}
|
|
15731
|
+
function artifactsRoot() {
|
|
15732
|
+
return process.env.ARGENT_ARTIFACTS_DIR ?? join3(tmpdir(), "argent-artifacts");
|
|
15733
|
+
}
|
|
15734
|
+
function artifactDir(deviceId) {
|
|
15735
|
+
const parts = [artifactsRoot(), projectSlug(), sessionId()];
|
|
15736
|
+
if (deviceId) parts.push(sanitizeSegment(deviceId));
|
|
15737
|
+
return join3(...parts);
|
|
15738
|
+
}
|
|
15739
|
+
async function resolveLocalFile(handle) {
|
|
15740
|
+
if (!handle.hostPath) return null;
|
|
15741
|
+
try {
|
|
15742
|
+
const st = await stat(handle.hostPath);
|
|
15743
|
+
if (handle.archive) {
|
|
15744
|
+
return st.isDirectory() ? handle.hostPath : null;
|
|
15745
|
+
}
|
|
15746
|
+
if (!st.isFile()) return null;
|
|
15747
|
+
if (st.size !== handle.size) return null;
|
|
15748
|
+
if (handle.mtimeMs != null && Math.round(st.mtimeMs) !== Math.round(handle.mtimeMs)) {
|
|
15749
|
+
return null;
|
|
15750
|
+
}
|
|
15751
|
+
return handle.hostPath;
|
|
15752
|
+
} catch {
|
|
15753
|
+
return null;
|
|
15754
|
+
}
|
|
15755
|
+
}
|
|
15756
|
+
async function downloadAndExtractArchive(handle, data, dir) {
|
|
15757
|
+
const tarball = join3(dir, `${sanitizeSegment(handle.filename)}.tar.gz`);
|
|
15758
|
+
try {
|
|
15759
|
+
await writeFile3(tarball, data);
|
|
15760
|
+
await execFileAsync("tar", ["-xzf", tarball, "-C", dir]);
|
|
15761
|
+
return join3(dir, handle.filename);
|
|
15762
|
+
} catch {
|
|
15763
|
+
return null;
|
|
15764
|
+
} finally {
|
|
15765
|
+
await rm(tarball, { force: true }).catch(() => {
|
|
15766
|
+
});
|
|
15767
|
+
}
|
|
15768
|
+
}
|
|
15769
|
+
async function materializeArtifacts(result, ctx) {
|
|
15770
|
+
const images = [];
|
|
15771
|
+
const fetchFn = ctx.fetchImpl ?? fetch;
|
|
15772
|
+
const authHeaders2 = ctx.authToken ? { Authorization: `Bearer ${ctx.authToken}` } : {};
|
|
15773
|
+
const dir = artifactDir(ctx.deviceId);
|
|
15774
|
+
let dirReady = false;
|
|
15775
|
+
async function ensureDir() {
|
|
15776
|
+
if (!dirReady) {
|
|
15777
|
+
await mkdir3(dir, { recursive: true });
|
|
15778
|
+
dirReady = true;
|
|
15779
|
+
}
|
|
15780
|
+
}
|
|
15781
|
+
async function walk(value) {
|
|
15782
|
+
if (isArtifactHandle(value)) {
|
|
15783
|
+
const localPath = await resolveLocalFile(value);
|
|
15784
|
+
if (localPath) {
|
|
15785
|
+
if (value.mimeType.startsWith("image/")) {
|
|
15786
|
+
images.push({ localPath, data: await readFile3(localPath), mimeType: value.mimeType });
|
|
15787
|
+
}
|
|
15788
|
+
return localPath;
|
|
15789
|
+
}
|
|
15790
|
+
try {
|
|
15791
|
+
const res = await fetchFn(`${ctx.toolsUrl}/artifacts/${value.id}`, {
|
|
15792
|
+
headers: authHeaders2
|
|
15793
|
+
});
|
|
15794
|
+
if (!res.ok) return null;
|
|
15795
|
+
const data = Buffer.from(await res.arrayBuffer());
|
|
15796
|
+
await ensureDir();
|
|
15797
|
+
if (value.archive === "tar.gz") {
|
|
15798
|
+
return await downloadAndExtractArchive(value, data, dir);
|
|
15799
|
+
}
|
|
15800
|
+
if (value.size > 0 && data.length !== value.size) return null;
|
|
15801
|
+
const downloadedPath = join3(dir, sanitizeSegment(value.filename));
|
|
15802
|
+
await writeFile3(downloadedPath, data);
|
|
15803
|
+
if (value.mimeType.startsWith("image/")) {
|
|
15804
|
+
images.push({ localPath: downloadedPath, data, mimeType: value.mimeType });
|
|
15805
|
+
}
|
|
15806
|
+
return downloadedPath;
|
|
15807
|
+
} catch {
|
|
15808
|
+
return null;
|
|
15809
|
+
}
|
|
15810
|
+
}
|
|
15811
|
+
if (Array.isArray(value)) {
|
|
15812
|
+
return Promise.all(value.map(walk));
|
|
15813
|
+
}
|
|
15814
|
+
if (value && typeof value === "object") {
|
|
15815
|
+
const out = {};
|
|
15816
|
+
for (const [k, v] of Object.entries(value)) {
|
|
15817
|
+
out[k] = await walk(v);
|
|
15818
|
+
}
|
|
15819
|
+
return out;
|
|
15820
|
+
}
|
|
15821
|
+
return value;
|
|
15822
|
+
}
|
|
15823
|
+
const rewritten = await walk(result);
|
|
15824
|
+
return { result: rewritten, images };
|
|
15825
|
+
}
|
|
15826
|
+
function getDeviceIdFromArgs(args) {
|
|
15827
|
+
if (!args || typeof args !== "object") return void 0;
|
|
15828
|
+
const rec = args;
|
|
15829
|
+
if (typeof rec.udid === "string") return rec.udid;
|
|
15830
|
+
if (typeof rec.device_id === "string") return rec.device_id;
|
|
15831
|
+
return void 0;
|
|
15602
15832
|
}
|
|
15603
15833
|
|
|
15604
15834
|
// ../argent-mcp/src/content.ts
|
|
15605
|
-
import { readFile as
|
|
15835
|
+
import { readFile as readFile4 } from "node:fs/promises";
|
|
15606
15836
|
var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
|
|
15837
|
+
function imageBlock(data, mimeType) {
|
|
15838
|
+
return { type: "image", data: data.toString("base64"), mimeType };
|
|
15839
|
+
}
|
|
15607
15840
|
async function fetchPngBytes(url) {
|
|
15608
15841
|
try {
|
|
15609
15842
|
const res = await fetch(url);
|
|
@@ -15616,22 +15849,38 @@ async function fetchPngBytes(url) {
|
|
|
15616
15849
|
return null;
|
|
15617
15850
|
}
|
|
15618
15851
|
}
|
|
15619
|
-
async function toMcpContent(result, outputHint, args) {
|
|
15620
|
-
|
|
15852
|
+
async function toMcpContent(result, outputHint, ctx, args) {
|
|
15853
|
+
const suppressImage = isRecord(args) && args.includeImageInContext === false;
|
|
15854
|
+
if (ctx) {
|
|
15855
|
+
const { result: rewritten, images } = await materializeArtifacts(result, ctx);
|
|
15856
|
+
if (outputHint === "image") {
|
|
15857
|
+
if (images.length > 0) {
|
|
15858
|
+
const saved = { type: "text", text: `Saved: ${images[0].localPath}` };
|
|
15859
|
+
if (suppressImage) return [saved];
|
|
15860
|
+
const blocks2 = images.map((img) => imageBlock(img.data, img.mimeType));
|
|
15861
|
+
blocks2.push(saved);
|
|
15862
|
+
return blocks2;
|
|
15863
|
+
}
|
|
15864
|
+
return legacyImageContent(rewritten, suppressImage);
|
|
15865
|
+
}
|
|
15866
|
+
const blocks = [{ type: "text", text: JSON.stringify(rewritten, null, 2) }];
|
|
15867
|
+
if (!suppressImage) for (const img of images) blocks.push(imageBlock(img.data, img.mimeType));
|
|
15868
|
+
return blocks;
|
|
15869
|
+
}
|
|
15870
|
+
if (outputHint === "image") {
|
|
15871
|
+
return legacyImageContent(result, suppressImage);
|
|
15872
|
+
}
|
|
15873
|
+
return [{ type: "text", text: JSON.stringify(result, null, 2) }];
|
|
15874
|
+
}
|
|
15875
|
+
async function legacyImageContent(result, suppressImage) {
|
|
15876
|
+
if (result && typeof result === "object" && "url" in result) {
|
|
15621
15877
|
const r = result;
|
|
15622
|
-
if (
|
|
15623
|
-
return [{ type: "text", text: `Saved: ${r.path}` }];
|
|
15878
|
+
if (suppressImage) {
|
|
15879
|
+
return [{ type: "text", text: `Saved: ${r.path ?? ""}` }];
|
|
15624
15880
|
}
|
|
15625
15881
|
const buf = await fetchPngBytes(r.url);
|
|
15626
15882
|
if (buf) {
|
|
15627
|
-
return [
|
|
15628
|
-
{
|
|
15629
|
-
type: "image",
|
|
15630
|
-
data: buf.toString("base64"),
|
|
15631
|
-
mimeType: "image/png"
|
|
15632
|
-
},
|
|
15633
|
-
{ type: "text", text: `Saved: ${r.path ?? ""}` }
|
|
15634
|
-
];
|
|
15883
|
+
return [imageBlock(buf, "image/png"), { type: "text", text: `Saved: ${r.path ?? ""}` }];
|
|
15635
15884
|
}
|
|
15636
15885
|
return [
|
|
15637
15886
|
{
|
|
@@ -15652,7 +15901,7 @@ function isScreenshotDiffResult(value) {
|
|
|
15652
15901
|
async function screenshotDiffToMcpContent(result) {
|
|
15653
15902
|
const blocks = [];
|
|
15654
15903
|
if (typeof result.contextDiffPath === "string") {
|
|
15655
|
-
const buf = await
|
|
15904
|
+
const buf = await readFile4(result.contextDiffPath);
|
|
15656
15905
|
blocks.push({
|
|
15657
15906
|
type: "image",
|
|
15658
15907
|
data: buf.toString("base64"),
|
|
@@ -15662,7 +15911,7 @@ async function screenshotDiffToMcpContent(result) {
|
|
|
15662
15911
|
blocks.push({ type: "text", text: result.summary });
|
|
15663
15912
|
return blocks;
|
|
15664
15913
|
}
|
|
15665
|
-
async function flowRunToMcpContent(result) {
|
|
15914
|
+
async function flowRunToMcpContent(result, ctx) {
|
|
15666
15915
|
const blocks = [];
|
|
15667
15916
|
if (result.executionPrerequisite) {
|
|
15668
15917
|
blocks.push({
|
|
@@ -15686,7 +15935,7 @@ async function flowRunToMcpContent(result) {
|
|
|
15686
15935
|
});
|
|
15687
15936
|
} else {
|
|
15688
15937
|
blocks.push({ type: "text", text: `[${num}] ${step.tool}` });
|
|
15689
|
-
const stepContent = await toMcpContent(step.result, step.outputHint, step.args);
|
|
15938
|
+
const stepContent = await toMcpContent(step.result, step.outputHint, ctx, step.args);
|
|
15690
15939
|
blocks.push(...stepContent);
|
|
15691
15940
|
}
|
|
15692
15941
|
}
|
|
@@ -15694,6 +15943,64 @@ async function flowRunToMcpContent(result) {
|
|
|
15694
15943
|
return blocks;
|
|
15695
15944
|
}
|
|
15696
15945
|
|
|
15946
|
+
// ../configuration-core/src/flags.ts
|
|
15947
|
+
import * as fs2 from "node:fs";
|
|
15948
|
+
import * as path3 from "node:path";
|
|
15949
|
+
import { homedir as homedir3 } from "node:os";
|
|
15950
|
+
var PROJECT_MARKERS = [".argent", ".git", "package.json"];
|
|
15951
|
+
function resolveProjectRoot(startDir) {
|
|
15952
|
+
const initial = path3.resolve(startDir);
|
|
15953
|
+
let current = initial;
|
|
15954
|
+
while (true) {
|
|
15955
|
+
for (const marker of PROJECT_MARKERS) {
|
|
15956
|
+
if (fs2.existsSync(path3.join(current, marker))) return current;
|
|
15957
|
+
}
|
|
15958
|
+
const parent = path3.dirname(current);
|
|
15959
|
+
if (parent === current) return initial;
|
|
15960
|
+
current = parent;
|
|
15961
|
+
}
|
|
15962
|
+
}
|
|
15963
|
+
function getFlagsPath(scope, options = {}) {
|
|
15964
|
+
const home = options.homeDir ?? homedir3();
|
|
15965
|
+
if (scope === "global") {
|
|
15966
|
+
return path3.join(home, ".argent", "flags.json");
|
|
15967
|
+
}
|
|
15968
|
+
const cwd = options.cwd ?? process.cwd();
|
|
15969
|
+
return path3.join(resolveProjectRoot(cwd), ".argent", "flags.json");
|
|
15970
|
+
}
|
|
15971
|
+
function readFlagsFile(filePath) {
|
|
15972
|
+
let raw;
|
|
15973
|
+
try {
|
|
15974
|
+
raw = fs2.readFileSync(filePath, "utf8");
|
|
15975
|
+
} catch {
|
|
15976
|
+
return {};
|
|
15977
|
+
}
|
|
15978
|
+
let parsed;
|
|
15979
|
+
try {
|
|
15980
|
+
parsed = JSON.parse(raw);
|
|
15981
|
+
} catch {
|
|
15982
|
+
return {};
|
|
15983
|
+
}
|
|
15984
|
+
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
15985
|
+
const flags = parsed.flags;
|
|
15986
|
+
if (!flags || typeof flags !== "object" || Array.isArray(flags)) return {};
|
|
15987
|
+
const out = {};
|
|
15988
|
+
for (const [k, v] of Object.entries(flags)) {
|
|
15989
|
+
if (typeof v === "boolean") out[k] = v;
|
|
15990
|
+
}
|
|
15991
|
+
return out;
|
|
15992
|
+
}
|
|
15993
|
+
function readFlags(scope, options = {}) {
|
|
15994
|
+
return readFlagsFile(getFlagsPath(scope, options));
|
|
15995
|
+
}
|
|
15996
|
+
function isFlagEnabled(name, options = {}) {
|
|
15997
|
+
const projectFlags = readFlags("project", options);
|
|
15998
|
+
if (Object.hasOwn(projectFlags, name)) return projectFlags[name];
|
|
15999
|
+
const globalFlags = readFlags("global", options);
|
|
16000
|
+
if (Object.hasOwn(globalFlags, name)) return globalFlags[name];
|
|
16001
|
+
return false;
|
|
16002
|
+
}
|
|
16003
|
+
|
|
15697
16004
|
// ../argent-mcp/src/auto-screenshot.ts
|
|
15698
16005
|
var AUTO_SCREENSHOT_TOOLS = /* @__PURE__ */ new Set([
|
|
15699
16006
|
"gesture-tap",
|
|
@@ -15726,9 +16033,8 @@ var AUTO_SCREENSHOT_DELAY_MS_BY_TOOL = {
|
|
|
15726
16033
|
"describe": 100
|
|
15727
16034
|
};
|
|
15728
16035
|
var DEFAULT_DELAY_MS = 1400;
|
|
15729
|
-
function autoScreenshotEnabled() {
|
|
15730
|
-
|
|
15731
|
-
return v === void 0 || v === "" || v === "1" || v.toLowerCase() === "true";
|
|
16036
|
+
function autoScreenshotEnabled(options) {
|
|
16037
|
+
return !isFlagEnabled("disable-auto-screenshot", options);
|
|
15732
16038
|
}
|
|
15733
16039
|
function getUdidFromArgs(args) {
|
|
15734
16040
|
if (args && typeof args === "object" && "udid" in args && typeof args.udid === "string") {
|
|
@@ -15799,36 +16105,46 @@ async function fetchWithReconnect(getUrl, reconnect, config2) {
|
|
|
15799
16105
|
throw lastError;
|
|
15800
16106
|
}
|
|
15801
16107
|
async function startMcpServer(options) {
|
|
16108
|
+
const autoScreenshotOn = autoScreenshotEnabled();
|
|
15802
16109
|
let TOOLS_URL;
|
|
15803
|
-
|
|
15804
|
-
|
|
16110
|
+
let AUTH_TOKEN;
|
|
16111
|
+
const resolved = await getResolvedToolsUrl();
|
|
16112
|
+
if (resolved.url) {
|
|
16113
|
+
TOOLS_URL = resolved.url;
|
|
16114
|
+
AUTH_TOKEN = resolved.token ?? "";
|
|
15805
16115
|
} else {
|
|
15806
16116
|
try {
|
|
15807
|
-
|
|
16117
|
+
const handle = await ensureToolsServer(options.paths);
|
|
16118
|
+
TOOLS_URL = handle.url;
|
|
16119
|
+
AUTH_TOKEN = handle.token;
|
|
15808
16120
|
} catch (err) {
|
|
15809
16121
|
process.stderr.write(`[argent] Failed to start tools server: ${err}
|
|
15810
16122
|
`);
|
|
15811
16123
|
process.exit(1);
|
|
15812
16124
|
}
|
|
15813
16125
|
}
|
|
16126
|
+
function authHeader() {
|
|
16127
|
+
return AUTH_TOKEN ? { Authorization: `Bearer ${AUTH_TOKEN}` } : {};
|
|
16128
|
+
}
|
|
15814
16129
|
let reconnectPromise = null;
|
|
15815
16130
|
async function reconnect() {
|
|
15816
|
-
if (
|
|
16131
|
+
if (await isRemoteRouted()) return;
|
|
15817
16132
|
if (!reconnectPromise) {
|
|
15818
|
-
reconnectPromise = ensureToolsServer(options.paths).then((
|
|
15819
|
-
TOOLS_URL = url;
|
|
16133
|
+
reconnectPromise = ensureToolsServer(options.paths).then((handle) => {
|
|
16134
|
+
TOOLS_URL = handle.url;
|
|
16135
|
+
AUTH_TOKEN = handle.token;
|
|
15820
16136
|
}).finally(() => {
|
|
15821
16137
|
reconnectPromise = null;
|
|
15822
16138
|
});
|
|
15823
16139
|
}
|
|
15824
16140
|
return reconnectPromise;
|
|
15825
16141
|
}
|
|
15826
|
-
const LOG_FILE2 = process.env.ARGENT_MCP_LOG ?? `${
|
|
16142
|
+
const LOG_FILE2 = process.env.ARGENT_MCP_LOG ?? `${homedir4()}/.argent/mcp-calls.log`;
|
|
15827
16143
|
let logDirReady = false;
|
|
15828
16144
|
async function spyLog(entry) {
|
|
15829
16145
|
try {
|
|
15830
16146
|
if (!logDirReady) {
|
|
15831
|
-
await
|
|
16147
|
+
await mkdir4(dirname2(LOG_FILE2), { recursive: true });
|
|
15832
16148
|
logDirReady = true;
|
|
15833
16149
|
}
|
|
15834
16150
|
await appendFile(LOG_FILE2, JSON.stringify(entry) + "\n");
|
|
@@ -15836,7 +16152,9 @@ async function startMcpServer(options) {
|
|
|
15836
16152
|
}
|
|
15837
16153
|
}
|
|
15838
16154
|
async function fetchTools() {
|
|
15839
|
-
const res = await fetchWithReconnect(() => `${TOOLS_URL}/tools`, reconnect
|
|
16155
|
+
const res = await fetchWithReconnect(() => `${TOOLS_URL}/tools`, reconnect, {
|
|
16156
|
+
init: { headers: authHeader() }
|
|
16157
|
+
});
|
|
15840
16158
|
const json = await res.json();
|
|
15841
16159
|
return json.tools;
|
|
15842
16160
|
}
|
|
@@ -15846,7 +16164,7 @@ async function startMcpServer(options) {
|
|
|
15846
16164
|
const res = await fetchWithReconnect(() => `${TOOLS_URL}/tools/${name}`, reconnect, {
|
|
15847
16165
|
init: {
|
|
15848
16166
|
method: "POST",
|
|
15849
|
-
headers: { "Content-Type": "application/json" },
|
|
16167
|
+
headers: { "Content-Type": "application/json", ...authHeader() },
|
|
15850
16168
|
body: JSON.stringify(args ?? {})
|
|
15851
16169
|
},
|
|
15852
16170
|
fetchTimeoutMs: meta2?.longRunning ? null : FETCH_TIMEOUT_MS
|
|
@@ -15897,21 +16215,30 @@ async function startMcpServer(options) {
|
|
|
15897
16215
|
isError: false,
|
|
15898
16216
|
result
|
|
15899
16217
|
});
|
|
16218
|
+
const ctx = {
|
|
16219
|
+
toolsUrl: TOOLS_URL,
|
|
16220
|
+
authToken: AUTH_TOKEN,
|
|
16221
|
+
deviceId: getDeviceIdFromArgs(params.arguments)
|
|
16222
|
+
};
|
|
15900
16223
|
let content;
|
|
15901
16224
|
if (params.name === "flow-execute" && result && typeof result === "object" && "flow" in result && "steps" in result) {
|
|
15902
|
-
content = await flowRunToMcpContent(result);
|
|
16225
|
+
content = await flowRunToMcpContent(result, ctx);
|
|
15903
16226
|
} else if (params.name === "screenshot-diff" && isScreenshotDiffResult(result)) {
|
|
15904
16227
|
content = await screenshotDiffToMcpContent(result);
|
|
15905
16228
|
} else {
|
|
15906
|
-
content = await toMcpContent(result, outputHint, params.arguments);
|
|
16229
|
+
content = await toMcpContent(result, outputHint, ctx, params.arguments);
|
|
15907
16230
|
}
|
|
15908
16231
|
const udid = getUdidFromArgs(params.arguments);
|
|
15909
|
-
if (
|
|
16232
|
+
if (autoScreenshotOn && udid && shouldAutoScreenshot(params.name)) {
|
|
15910
16233
|
const delayMs = getAutoScreenshotDelayMs(params.name);
|
|
15911
16234
|
if (delayMs > 0) await new Promise((r) => setTimeout(r, delayMs));
|
|
15912
16235
|
try {
|
|
15913
16236
|
const screenshotResult = await callTool("screenshot", { udid });
|
|
15914
|
-
const screenshotContent = await toMcpContent(screenshotResult.result, "image", {
|
|
16237
|
+
const screenshotContent = await toMcpContent(screenshotResult.result, "image", {
|
|
16238
|
+
toolsUrl: TOOLS_URL,
|
|
16239
|
+
authToken: AUTH_TOKEN,
|
|
16240
|
+
deviceId: udid
|
|
16241
|
+
});
|
|
15915
16242
|
const hasImage = screenshotContent.some((b) => b.type === "image");
|
|
15916
16243
|
if (hasImage) {
|
|
15917
16244
|
content = [
|
|
@@ -15951,14 +16278,17 @@ async function startMcpServer(options) {
|
|
|
15951
16278
|
}
|
|
15952
16279
|
});
|
|
15953
16280
|
await server.connect(new StdioServerTransport());
|
|
15954
|
-
if (!
|
|
16281
|
+
if (!await isRemoteRouted()) {
|
|
15955
16282
|
const HEALTH_INTERVAL_MS = 3e4;
|
|
15956
16283
|
const healthInterval = setInterval(async () => {
|
|
15957
16284
|
try {
|
|
15958
16285
|
const controller = new AbortController();
|
|
15959
16286
|
const timer = setTimeout(() => controller.abort(), 3e3);
|
|
15960
16287
|
try {
|
|
15961
|
-
const res = await fetch(`${TOOLS_URL}/tools`, {
|
|
16288
|
+
const res = await fetch(`${TOOLS_URL}/tools`, {
|
|
16289
|
+
signal: controller.signal,
|
|
16290
|
+
headers: authHeader()
|
|
16291
|
+
});
|
|
15962
16292
|
if (!res.ok) throw new Error(`health check returned ${res.status}`);
|
|
15963
16293
|
} finally {
|
|
15964
16294
|
clearTimeout(timer);
|
|
@@ -15971,9 +16301,9 @@ async function startMcpServer(options) {
|
|
|
15971
16301
|
}, HEALTH_INTERVAL_MS);
|
|
15972
16302
|
healthInterval.unref();
|
|
15973
16303
|
}
|
|
15974
|
-
if (process.env.
|
|
16304
|
+
if (process.env.ARGENT_TOOL_SERVER_SHUTDOWN_ON_MCP_EXIT === "1") {
|
|
15975
16305
|
process.stdin.on("close", () => {
|
|
15976
|
-
fetch(`${TOOLS_URL}/shutdown`, { method: "POST" }).catch(() => {
|
|
16306
|
+
fetch(`${TOOLS_URL}/shutdown`, { method: "POST", headers: authHeader() }).catch(() => {
|
|
15977
16307
|
});
|
|
15978
16308
|
setTimeout(() => process.exit(0), 5e3);
|
|
15979
16309
|
});
|