@lucid-agents/taskmarket 1.17.0 → 1.17.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,154 @@
1
+ import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
+ import {
3
+ buyComputeCredit,
4
+ fetchComputePricing,
5
+ listPendingComputePurchases,
6
+ loadComputeCreditConfig,
7
+ onComputeBalanceObserved,
8
+ readComputeCreditStatus
9
+ } from "./chunk-52Q2DCZN.js";
10
+ import {
11
+ AgentLaunchError
12
+ } from "./chunk-YM4S6IE4.js";
13
+ import {
14
+ burnRate,
15
+ computeLow
16
+ } from "./chunk-73RXOYME.js";
17
+
18
+ // ../../packages/remote-tools/dist/backend.js
19
+ var SANDBOX_TOOL_USER = "agent";
20
+ var SandboxUnavailableError = class extends Error {
21
+ reason;
22
+ constructor(reason, options) {
23
+ super(`Sandbox unavailable, tools are disabled (no local fallback): ${reason}`, options);
24
+ this.name = "SandboxUnavailableError";
25
+ this.reason = reason;
26
+ }
27
+ };
28
+
29
+ // src/lib/agent/compute.ts
30
+ async function readComputeView(options = {}) {
31
+ const warning = options.warning ?? null;
32
+ const empty = {
33
+ availableCredits: null,
34
+ reservedCredits: null,
35
+ burnCreditsPerHour: null,
36
+ low: warning === "low_credit" || warning === "credit_exhausted",
37
+ warning,
38
+ pendingPurchases: 0,
39
+ autoTopUp: null
40
+ };
41
+ let config;
42
+ try {
43
+ config = await loadComputeCreditConfig();
44
+ } catch (error) {
45
+ return { configured: false, ...empty, error: error.message };
46
+ }
47
+ if (!config) return { configured: false, ...empty };
48
+ try {
49
+ let status = await readComputeCreditStatus();
50
+ if (options.observe && await options.observe({ availableCredits: status.balance.availableCredits })) {
51
+ status = await readComputeCreditStatus();
52
+ }
53
+ const burn = burnRate(status.debits);
54
+ const autoTopUp = config.autoTopUp ? {
55
+ thresholdCredits: config.autoTopUp.thresholdCredits,
56
+ amountUsd: config.autoTopUp.amountUsd,
57
+ maxChunksPerDay: config.autoTopUp.maxChunksPerDay
58
+ } : null;
59
+ return {
60
+ configured: true,
61
+ availableCredits: status.balance.availableCredits,
62
+ reservedCredits: status.balance.reservedCredits,
63
+ burnCreditsPerHour: burn,
64
+ low: computeLow({
65
+ availableCredits: status.balance.availableCredits,
66
+ burnCreditsPerHour: burn,
67
+ warning,
68
+ ...autoTopUp ? { thresholdCredits: autoTopUp.thresholdCredits } : {}
69
+ }),
70
+ warning,
71
+ pendingPurchases: (await listPendingComputePurchases()).length,
72
+ autoTopUp
73
+ };
74
+ } catch (error) {
75
+ return {
76
+ configured: true,
77
+ ...empty,
78
+ error: error.code ?? error.message
79
+ };
80
+ }
81
+ }
82
+ async function computeAllocationId() {
83
+ return (await loadComputeCreditConfig().catch(() => void 0))?.allocationId;
84
+ }
85
+ async function computeCreditOffer() {
86
+ const pricing = await fetchComputePricing();
87
+ return {
88
+ usdPerCredit: pricing.usdPerCredit,
89
+ denominations: pricing.denominations.map((entry) => ({
90
+ amountUsd: entry.amountUsd,
91
+ credits: entry.credits,
92
+ default: entry.default
93
+ }))
94
+ };
95
+ }
96
+ async function buyComputeChunk(amountUsd) {
97
+ const pricing = await fetchComputePricing();
98
+ const size = amountUsd ?? (pricing.denominations.find((entry) => entry.default) ?? pricing.denominations[0])?.amountUsd;
99
+ if (size === void 0) {
100
+ throw new AgentLaunchError("invalid_amount", "The API sells no compute credit sizes");
101
+ }
102
+ const bought = await buyComputeCredit({ amountUsd: size });
103
+ return { purchase: bought.purchase, balance: bought.balance, amountUsd: size };
104
+ }
105
+ function computeCreditObserver(log) {
106
+ return async (observation) => {
107
+ try {
108
+ const { topUp } = await onComputeBalanceObserved(observation);
109
+ if (topUp.action === "bought") log("Compute credit auto-top-up bought one chunk");
110
+ else if (topUp.action === "skipped")
111
+ log(`Compute credit auto-top-up skipped: ${topUp.reason}`);
112
+ return topUp.action === "bought";
113
+ } catch (error) {
114
+ log(
115
+ `Compute credit auto-top-up failed: ${error.code ?? error.message}`
116
+ );
117
+ return false;
118
+ }
119
+ };
120
+ }
121
+ function insufficientCredit(error) {
122
+ return error instanceof SandboxUnavailableError && /\binsufficient_credit\b/.test(error.reason);
123
+ }
124
+ async function openSandbox(createBroker, observe) {
125
+ const first = createBroker();
126
+ try {
127
+ await first.connect("start");
128
+ return first;
129
+ } catch (error) {
130
+ if (!insufficientCredit(error)) throw error;
131
+ if (!await observe({ creditWarning: "credit_exhausted" })) {
132
+ throw new AgentLaunchError("insufficient_credit", error.reason);
133
+ }
134
+ }
135
+ const retry = createBroker();
136
+ try {
137
+ await retry.connect("start");
138
+ return retry;
139
+ } catch (error) {
140
+ if (insufficientCredit(error)) throw new AgentLaunchError("insufficient_credit", error.reason);
141
+ throw error;
142
+ }
143
+ }
144
+
145
+ export {
146
+ SANDBOX_TOOL_USER,
147
+ SandboxUnavailableError,
148
+ readComputeView,
149
+ computeAllocationId,
150
+ computeCreditOffer,
151
+ buyComputeChunk,
152
+ computeCreditObserver,
153
+ openSandbox
154
+ };
@@ -1,8 +1,7 @@
1
1
  import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
2
  import {
3
3
  AgentLaunchError
4
- } from "./chunk-O27J7ZPH.js";
5
- import "./chunk-S7SJMJ2I.js";
4
+ } from "./chunk-YM4S6IE4.js";
6
5
 
7
6
  // src/lib/agent/compatibility.ts
8
7
  import { existsSync, readFileSync } from "fs";
@@ -123,11 +122,12 @@ function assertAgentCompatibility(options = {}) {
123
122
  }
124
123
  return runtime;
125
124
  }
125
+
126
126
  export {
127
127
  AGENT_COMPATIBILITY,
128
128
  agentCompatibility,
129
- assertAgentCompatibility,
130
- findInstalledPackage,
131
129
  nodeVersionSupported,
132
- piRuntime
130
+ findInstalledPackage,
131
+ piRuntime,
132
+ assertAgentCompatibility
133
133
  };
@@ -0,0 +1,181 @@
1
+ import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
+ import {
3
+ computeAllocationId,
4
+ readComputeView
5
+ } from "./chunk-LDCFKFF6.js";
6
+ import {
7
+ allocationViews,
8
+ channelView,
9
+ getAgentPolicyPath,
10
+ openAgentChannels,
11
+ uncertainPurchases,
12
+ withdrawalHatch
13
+ } from "./chunk-GJQUALXN.js";
14
+ import {
15
+ SandboxListOutputSchema,
16
+ apiUrl
17
+ } from "./chunk-FRV3ZHUR.js";
18
+ import {
19
+ AgentLaunchError
20
+ } from "./chunk-YM4S6IE4.js";
21
+ import {
22
+ AGENT_VIEW_VERSION,
23
+ blockingPurchases,
24
+ channelLines,
25
+ computeLines,
26
+ formatUsdc
27
+ } from "./chunk-73RXOYME.js";
28
+
29
+ // src/lib/agent/status.ts
30
+ async function listSandboxes(credentials, fetchImpl = fetch) {
31
+ const response = await fetchImpl(`${apiUrl().replace(/\/+$/, "")}/api/sandboxes/list`, {
32
+ method: "POST",
33
+ redirect: "error",
34
+ headers: { "content-type": "application/json" },
35
+ body: JSON.stringify(credentials),
36
+ signal: AbortSignal.timeout(15e3)
37
+ });
38
+ if (!response.ok) {
39
+ await response.body?.cancel().catch(() => void 0);
40
+ throw new Error(`the sandbox broker answered ${response.status}`);
41
+ }
42
+ const parsed = SandboxListOutputSchema.safeParse(await response.json());
43
+ if (!parsed.success) throw new Error("the sandbox list is malformed");
44
+ return parsed.data.sandboxes.map((s) => ({
45
+ sandboxId: s.sandboxId,
46
+ purpose: s.purpose,
47
+ state: s.state,
48
+ meteredSeconds: s.meteredSeconds,
49
+ lastActiveAt: s.lastActiveAt,
50
+ expiresAt: s.expiresAt,
51
+ creditWarning: s.creditWarning,
52
+ creditGraceUntil: s.creditGraceUntil
53
+ }));
54
+ }
55
+ function message(error) {
56
+ return error.code ?? error.message;
57
+ }
58
+ async function collectAgentStatus(deps = {}) {
59
+ const now = deps.now ?? (() => /* @__PURE__ */ new Date());
60
+ const view = {
61
+ version: AGENT_VIEW_VERSION,
62
+ kind: "taskmarket.agent.status",
63
+ generatedAt: now().toISOString(),
64
+ wallet: null,
65
+ policy: { state: "missing", path: getAgentPolicyPath(), models: [] },
66
+ channels: [],
67
+ purchases: { uncertain: [] },
68
+ inference: { blocked: false, reasons: [] },
69
+ allocations: [],
70
+ sandbox: { state: "no_wallet", sandboxes: [] },
71
+ compute: {
72
+ configured: false,
73
+ availableCredits: null,
74
+ reservedCredits: null,
75
+ burnCreditsPerHour: null,
76
+ low: false,
77
+ warning: null,
78
+ pendingPurchases: 0,
79
+ autoTopUp: null
80
+ }
81
+ };
82
+ const reasons = view.inference.reasons;
83
+ let credentials;
84
+ try {
85
+ const opened = await (deps.openChannels ?? openAgentChannels)();
86
+ view.wallet = { address: opened.keystore.walletAddress };
87
+ credentials = { deviceId: opened.keystore.deviceId, apiToken: opened.keystore.apiToken };
88
+ view.policy = { state: "approved", path: view.policy.path, models: [...opened.policy.models] };
89
+ const statuses = /* @__PURE__ */ new Map();
90
+ for (const rail of opened.rails) {
91
+ const status = await rail.payer.channelStatus();
92
+ statuses.set(rail.rail.id, status);
93
+ const channel = await channelView(rail, status);
94
+ view.channels.push({ ...channel, withdrawal: withdrawalHatch(channel) });
95
+ }
96
+ for (const rail of opened.retired ?? []) {
97
+ const channel = await channelView(rail, void 0, { retired: true });
98
+ view.channels.push({ ...channel, withdrawal: withdrawalHatch(channel) });
99
+ }
100
+ view.purchases.uncertain = [
101
+ ...await uncertainPurchases(opened.rails, statuses),
102
+ ...await uncertainPurchases(opened.retired ?? [])
103
+ ];
104
+ view.allocations = await allocationViews(opened.rails, [
105
+ await (deps.computeAllocation ?? computeAllocationId)()
106
+ ]);
107
+ } catch (error) {
108
+ if (error instanceof AgentLaunchError && error.code === "wallet_required") {
109
+ reasons.push("no wallet: run taskmarket wallet import");
110
+ } else if (error instanceof AgentLaunchError && error.code === "setup_required") {
111
+ reasons.push("no approved spending policy");
112
+ } else {
113
+ view.policy = { ...view.policy, state: "invalid", error: message(error) };
114
+ reasons.push(`the spending policy cannot be used: ${message(error)}`);
115
+ }
116
+ }
117
+ const active = view.channels.filter((channel) => !channel.retired);
118
+ for (const id of blockingPurchases({ channels: active, unresolved: [] })) {
119
+ reasons.push(`purchase ${id} is unresolved: taskmarket agent reconcile ${id}`);
120
+ }
121
+ for (const channel of active) {
122
+ if (channel.state !== "open") reasons.push(`channel ${channel.rail} is ${channel.state}`);
123
+ }
124
+ if (credentials) {
125
+ try {
126
+ view.sandbox = {
127
+ state: "ok",
128
+ sandboxes: await (deps.listSandboxes ?? listSandboxes)(credentials)
129
+ };
130
+ } catch (error) {
131
+ view.sandbox = { state: "unavailable", sandboxes: [], error: message(error) };
132
+ }
133
+ }
134
+ const warning = view.sandbox.sandboxes.find((s) => s.purpose === "interactive" && s.creditWarning)?.creditWarning ?? null;
135
+ view.compute = await (deps.readCompute ?? readComputeView)({ warning });
136
+ view.inference.blocked = reasons.length > 0;
137
+ return view;
138
+ }
139
+ function statusLines(view) {
140
+ const lines = [
141
+ `taskmarket agent status (view v${view.version}, ${view.generatedAt})`,
142
+ view.wallet ? `Wallet: ${view.wallet.address}` : "Wallet: none",
143
+ `Spending policy: ${view.policy.state}${view.policy.models.length ? ` (${view.policy.models.join(", ")})` : ""}${view.policy.error ? `: ${view.policy.error}` : ""}`
144
+ ];
145
+ for (const channel of view.channels) {
146
+ lines.push(...channelLines(channel));
147
+ if (channel.state === "blocked" || channel.state === "withdrawal_pending") {
148
+ lines.push(` ${channel.withdrawal.note}`);
149
+ }
150
+ }
151
+ if (view.purchases.uncertain.length === 0) {
152
+ lines.push("Purchases with an unknown outcome: none");
153
+ } else {
154
+ lines.push("Purchases with an unknown outcome (never resent; reconcile them):");
155
+ for (const purchase of view.purchases.uncertain) {
156
+ lines.push(
157
+ ` ${purchase.invocationId} on ${purchase.rail}: ${purchase.state}, at most ${formatUsdc(purchase.authorizedAtomic)}${purchase.blocking ? ", blocks new turns" : ""}`
158
+ );
159
+ }
160
+ }
161
+ lines.push(
162
+ view.inference.blocked ? `New inference: blocked (${view.inference.reasons.join("; ")})` : "New inference: ready"
163
+ );
164
+ if (view.sandbox.state === "ok") {
165
+ lines.push(
166
+ view.sandbox.sandboxes.length === 0 ? "Sandbox: none" : `Sandbox: ${view.sandbox.sandboxes.map(
167
+ (s) => `${s.sandboxId} ${s.purpose} ${s.state}${s.creditWarning ? ` (${s.creditWarning})` : ""}`
168
+ ).join("; ")}`
169
+ );
170
+ } else if (view.sandbox.state === "unavailable") {
171
+ lines.push(`Sandbox: unavailable (${view.sandbox.error})`);
172
+ }
173
+ lines.push(...computeLines(view.compute));
174
+ return lines;
175
+ }
176
+
177
+ export {
178
+ listSandboxes,
179
+ collectAgentStatus,
180
+ statusLines
181
+ };
@@ -2,9 +2,10 @@ import { createRequire as __tmCreateRequire } from 'node:module'; import { fileU
2
2
 
3
3
  // src/lib/agent/errors.ts
4
4
  var AgentLaunchError = class extends Error {
5
- constructor(code, message) {
5
+ constructor(code, message, details) {
6
6
  super(message);
7
7
  this.code = code;
8
+ this.details = details;
8
9
  this.name = "AgentLaunchError";
9
10
  }
10
11
  };
@@ -0,0 +1,19 @@
1
+ import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
+ import {
3
+ AGENT_COMPATIBILITY,
4
+ agentCompatibility,
5
+ assertAgentCompatibility,
6
+ findInstalledPackage,
7
+ nodeVersionSupported,
8
+ piRuntime
9
+ } from "./chunk-OKZ2NRYZ.js";
10
+ import "./chunk-YM4S6IE4.js";
11
+ import "./chunk-S7SJMJ2I.js";
12
+ export {
13
+ AGENT_COMPATIBILITY,
14
+ agentCompatibility,
15
+ assertAgentCompatibility,
16
+ findInstalledPackage,
17
+ nodeVersionSupported,
18
+ piRuntime
19
+ };
@@ -0,0 +1,216 @@
1
+ import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
+ import {
3
+ AGENT_COMPATIBILITY,
4
+ assertAgentCompatibility,
5
+ nodeVersionSupported
6
+ } from "./chunk-OKZ2NRYZ.js";
7
+ import {
8
+ listSandboxes
9
+ } from "./chunk-XXD7TWLP.js";
10
+ import "./chunk-LDCFKFF6.js";
11
+ import "./chunk-52Q2DCZN.js";
12
+ import {
13
+ channelView,
14
+ loadAgentPolicy,
15
+ openAgentChannels
16
+ } from "./chunk-GJQUALXN.js";
17
+ import {
18
+ apiUrl,
19
+ getX402PolicyPath,
20
+ keystoreExists,
21
+ loadKeystore,
22
+ loadX402Policy,
23
+ rpcUrlForNetwork
24
+ } from "./chunk-FRV3ZHUR.js";
25
+ import "./chunk-YM4S6IE4.js";
26
+ import {
27
+ AGENT_VIEW_VERSION
28
+ } from "./chunk-73RXOYME.js";
29
+ import "./chunk-S7SJMJ2I.js";
30
+
31
+ // src/lib/agent/doctor.ts
32
+ var CHAIN_IDS = { "eip155:8453": "0x2105", "eip155:84532": "0x14a34" };
33
+ function reason(error) {
34
+ return error.code ?? error.message;
35
+ }
36
+ function loopback(url) {
37
+ return ["localhost", "127.0.0.1", "[::1]"].includes(url.hostname);
38
+ }
39
+ async function runDoctor(deps = {}) {
40
+ const fetchImpl = deps.fetch ?? fetch;
41
+ const nodeVersion = deps.nodeVersion ?? process.versions.node;
42
+ const apiBase = deps.apiUrl ?? apiUrl();
43
+ const checks = [];
44
+ const add = (id, status, summary) => checks.push({ id, status, summary });
45
+ const get = (url, init = {}) => fetchImpl(url, { redirect: "error", signal: AbortSignal.timeout(1e4), ...init });
46
+ add(
47
+ "node",
48
+ nodeVersionSupported(nodeVersion) ? "pass" : "fail",
49
+ `Node ${nodeVersion}; taskmarket agent needs ${AGENT_COMPATIBILITY.node.minimum} or later`
50
+ );
51
+ try {
52
+ (deps.checkRuntime ?? (() => assertAgentCompatibility({ nodeVersion: "99.0.0" })))();
53
+ add(
54
+ "runtime",
55
+ "pass",
56
+ `Bundled Pi ${AGENT_COMPATIBILITY.pi.version} and pi-xgate ${AGENT_COMPATIBILITY.piXgate.version} match this build`
57
+ );
58
+ } catch (error) {
59
+ add("runtime", "fail", error.message);
60
+ }
61
+ let credentials;
62
+ let walletAddress;
63
+ if (await keystoreExists()) {
64
+ const keystore = await loadKeystore();
65
+ credentials = { deviceId: keystore.deviceId, apiToken: keystore.apiToken };
66
+ walletAddress = keystore.walletAddress;
67
+ add("wallet", "pass", `Wallet ${keystore.walletAddress} (not unlocked)`);
68
+ } else {
69
+ add("wallet", "fail", "No wallet; run taskmarket wallet import");
70
+ }
71
+ let policy;
72
+ try {
73
+ policy = await loadAgentPolicy();
74
+ const foreign = policy.rails.filter(
75
+ (rail) => walletAddress && rail.wallet.toLowerCase() !== walletAddress.toLowerCase()
76
+ );
77
+ add(
78
+ "policy",
79
+ foreign.length > 0 ? "fail" : "pass",
80
+ foreign.length > 0 ? `Rail ${foreign[0].id} pays from ${foreign[0].wallet}, not this wallet` : `${policy.models.length} approved model(s), ${policy.rails.length} rail(s)`
81
+ );
82
+ } catch (error) {
83
+ add("policy", "fail", error.message);
84
+ }
85
+ const rpcUrls = /* @__PURE__ */ new Map();
86
+ if (policy) {
87
+ try {
88
+ const x402Policy = await loadX402Policy(getX402PolicyPath());
89
+ for (const rail of policy.rails)
90
+ rpcUrls.set(rail.network, rpcUrlForNetwork(x402Policy, rail.network));
91
+ add("payment_policy", "pass", `RPC configured for ${[...rpcUrls.keys()].join(", ")}`);
92
+ } catch (error) {
93
+ add("payment_policy", "fail", reason(error));
94
+ }
95
+ } else {
96
+ add("payment_policy", "skip", "No spending policy to check");
97
+ }
98
+ if (policy && rpcUrls.size > 0 && walletAddress) {
99
+ try {
100
+ const opened = await openAgentChannels();
101
+ const problems = [];
102
+ for (const rail of opened.rails) {
103
+ const channel = await channelView(rail);
104
+ if (channel.pendingOperation) {
105
+ problems.push(
106
+ `${channel.rail} has an unresolved ${channel.pendingOperation.kind} ${channel.pendingOperation.id}`
107
+ );
108
+ } else if (channel.state !== "open") {
109
+ problems.push(`${channel.rail} is ${channel.state}`);
110
+ }
111
+ }
112
+ add(
113
+ "channels",
114
+ problems.length > 0 ? "warn" : "pass",
115
+ problems.length > 0 ? problems.join("; ") : `${opened.rails.length} channel(s) open, nothing unresolved`
116
+ );
117
+ } catch (error) {
118
+ add("channels", "fail", reason(error));
119
+ }
120
+ } else {
121
+ add("channels", "skip", "Needs a wallet and a usable spending policy");
122
+ }
123
+ try {
124
+ const response = await get(`${apiBase.replace(/\/+$/, "")}/api/compute/pricing`);
125
+ await response.body?.cancel().catch(() => void 0);
126
+ add(
127
+ "api",
128
+ response.ok ? "pass" : "fail",
129
+ `${new URL(apiBase).origin} answered ${response.status}`
130
+ );
131
+ } catch (error) {
132
+ add("api", "fail", `${apiBase} is unreachable: ${reason(error)}`);
133
+ }
134
+ if (credentials) {
135
+ try {
136
+ const sandboxes = await listSandboxes(credentials, fetchImpl);
137
+ add(
138
+ "sandbox",
139
+ "pass",
140
+ `The sandbox broker answered; ${sandboxes.length} sandbox(es) on this wallet`
141
+ );
142
+ } catch (error) {
143
+ add("sandbox", "fail", `The sandbox broker is unavailable: ${reason(error)}`);
144
+ }
145
+ } else {
146
+ add("sandbox", "skip", "Needs a wallet");
147
+ }
148
+ const api = new URL(apiBase);
149
+ add(
150
+ "host_key",
151
+ api.protocol === "https:" ? "pass" : loopback(api) ? "warn" : "fail",
152
+ api.protocol === "https:" ? "Sandbox host keys are pinned per connection (ssh-ed25519 only) from the broker over HTTPS; never trusted on first use" : loopback(api) ? "The API is a local development server without TLS; host keys are only as trustworthy as it is" : `The API ${api.origin} is not HTTPS, so the sandbox host keys it returns cannot be trusted`
153
+ );
154
+ if (policy) {
155
+ for (const origin of new Set(policy.rails.map((rail) => rail.origin))) {
156
+ try {
157
+ const response = await get(`${origin}/v1/models`, {
158
+ headers: { accept: "application/json" }
159
+ });
160
+ await response.body?.cancel().catch(() => void 0);
161
+ add(
162
+ "xgate",
163
+ response.ok ? "pass" : "fail",
164
+ `${origin} catalog answered ${response.status}`
165
+ );
166
+ } catch (error) {
167
+ add("xgate", "fail", `${origin} is unreachable: ${reason(error)}`);
168
+ }
169
+ }
170
+ for (const [network, url] of rpcUrls) {
171
+ try {
172
+ const response = await get(url, {
173
+ method: "POST",
174
+ headers: { "content-type": "application/json" },
175
+ body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "eth_chainId", params: [] })
176
+ });
177
+ const body = await response.json();
178
+ const expected = CHAIN_IDS[network];
179
+ add(
180
+ "rpc",
181
+ body.result === expected ? "pass" : "fail",
182
+ body.result === expected ? `${network} RPC reachable` : `${network} RPC answered chain ${String(body.result)}, expected ${expected}`
183
+ );
184
+ } catch (error) {
185
+ add("rpc", "fail", `${network} RPC is unreachable: ${reason(error)}`);
186
+ }
187
+ }
188
+ } else {
189
+ add("xgate", "skip", "No spending policy names an xgate origin");
190
+ }
191
+ return {
192
+ version: AGENT_VIEW_VERSION,
193
+ kind: "taskmarket.agent.doctor",
194
+ ok: !checks.some((check) => check.status === "fail"),
195
+ compatibility: {
196
+ node: nodeVersion,
197
+ nodeMinimum: AGENT_COMPATIBILITY.node.minimum,
198
+ pi: AGENT_COMPATIBILITY.pi.version,
199
+ piXgate: AGENT_COMPATIBILITY.piXgate.version,
200
+ protocol: AGENT_COMPATIBILITY.protocol
201
+ },
202
+ checks
203
+ };
204
+ }
205
+ function doctorLines(report) {
206
+ return [
207
+ `taskmarket agent doctor (view v${report.version}): ${report.ok ? "ready" : "problems found"}`,
208
+ ...report.checks.map(
209
+ (check) => `${check.status.toUpperCase().padEnd(4)} ${check.id}: ${check.summary}`
210
+ )
211
+ ];
212
+ }
213
+ export {
214
+ doctorLines,
215
+ runDoctor
216
+ };
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __tmCreateRequire } from 'node:module'; import { fileURLToPath as __tmFileURLToPath } from 'node:url'; import { dirname as __tmDirname } from 'node:path'; const require = __tmCreateRequire(import.meta.url); const __tmFilename = __tmFileURLToPath(import.meta.url); const __tmDirnameValue = __tmDirname(__tmFilename);
2
2
  import {
3
3
  AgentLaunchError
4
- } from "./chunk-O27J7ZPH.js";
4
+ } from "./chunk-YM4S6IE4.js";
5
5
  import "./chunk-S7SJMJ2I.js";
6
6
  export {
7
7
  AgentLaunchError