@tasknet-protocol/cli 0.5.0 → 0.7.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/dist/index.js +668 -55
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -9,6 +9,68 @@ var __export = (target, all) => {
|
|
|
9
9
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
+
// src/config.ts
|
|
13
|
+
var config_exports = {};
|
|
14
|
+
__export(config_exports, {
|
|
15
|
+
clearConfig: () => clearConfig,
|
|
16
|
+
config: () => config,
|
|
17
|
+
getConfig: () => getConfig,
|
|
18
|
+
getConfigPath: () => getConfigPath,
|
|
19
|
+
setConfig: () => setConfig
|
|
20
|
+
});
|
|
21
|
+
import Conf from "conf";
|
|
22
|
+
function getConfig() {
|
|
23
|
+
return {
|
|
24
|
+
apiUrl: config.get("apiUrl"),
|
|
25
|
+
apiKey: config.get("apiKey"),
|
|
26
|
+
network: config.get("network"),
|
|
27
|
+
rpcUrl: config.get("rpcUrl"),
|
|
28
|
+
packageId: config.get("packageId"),
|
|
29
|
+
walrusAggregator: config.get("walrusAggregator"),
|
|
30
|
+
walrusPublisher: config.get("walrusPublisher"),
|
|
31
|
+
agentId: config.get("agentId"),
|
|
32
|
+
agentName: config.get("agentName"),
|
|
33
|
+
outputFormat: config.get("outputFormat")
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function setConfig(key, value) {
|
|
37
|
+
config.set(key, value);
|
|
38
|
+
}
|
|
39
|
+
function clearConfig() {
|
|
40
|
+
config.clear();
|
|
41
|
+
}
|
|
42
|
+
function getConfigPath() {
|
|
43
|
+
return config.path;
|
|
44
|
+
}
|
|
45
|
+
var defaults, config;
|
|
46
|
+
var init_config = __esm({
|
|
47
|
+
"src/config.ts"() {
|
|
48
|
+
"use strict";
|
|
49
|
+
defaults = {
|
|
50
|
+
apiUrl: "http://localhost:3000",
|
|
51
|
+
network: "testnet",
|
|
52
|
+
outputFormat: "table"
|
|
53
|
+
};
|
|
54
|
+
config = new Conf({
|
|
55
|
+
projectName: "tasknet-cli",
|
|
56
|
+
projectVersion: "0.6.0",
|
|
57
|
+
defaults,
|
|
58
|
+
schema: {
|
|
59
|
+
apiUrl: { type: "string" },
|
|
60
|
+
apiKey: { type: "string" },
|
|
61
|
+
network: { type: "string", enum: ["mainnet", "testnet", "devnet", "localnet"] },
|
|
62
|
+
rpcUrl: { type: "string" },
|
|
63
|
+
packageId: { type: "string" },
|
|
64
|
+
walrusAggregator: { type: "string" },
|
|
65
|
+
walrusPublisher: { type: "string" },
|
|
66
|
+
agentId: { type: "string" },
|
|
67
|
+
agentName: { type: "string" },
|
|
68
|
+
outputFormat: { type: "string", enum: ["table", "json"] }
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
12
74
|
// ../../node_modules/.pnpm/@noble+hashes@1.8.0/node_modules/@noble/hashes/esm/cryptoNode.js
|
|
13
75
|
import * as nc from "crypto";
|
|
14
76
|
var crypto;
|
|
@@ -12408,55 +12470,11 @@ var init_cryptography = __esm({
|
|
|
12408
12470
|
import { Command as Command2 } from "commander";
|
|
12409
12471
|
import chalk3 from "chalk";
|
|
12410
12472
|
|
|
12411
|
-
// src/config.ts
|
|
12412
|
-
|
|
12413
|
-
var defaults = {
|
|
12414
|
-
apiUrl: "http://localhost:3000",
|
|
12415
|
-
network: "testnet",
|
|
12416
|
-
outputFormat: "table"
|
|
12417
|
-
};
|
|
12418
|
-
var config = new Conf({
|
|
12419
|
-
projectName: "tasknet-cli",
|
|
12420
|
-
projectVersion: "0.5.0",
|
|
12421
|
-
defaults,
|
|
12422
|
-
schema: {
|
|
12423
|
-
apiUrl: { type: "string" },
|
|
12424
|
-
apiKey: { type: "string" },
|
|
12425
|
-
network: { type: "string", enum: ["mainnet", "testnet", "devnet", "localnet"] },
|
|
12426
|
-
rpcUrl: { type: "string" },
|
|
12427
|
-
packageId: { type: "string" },
|
|
12428
|
-
walrusAggregator: { type: "string" },
|
|
12429
|
-
walrusPublisher: { type: "string" },
|
|
12430
|
-
agentId: { type: "string" },
|
|
12431
|
-
agentName: { type: "string" },
|
|
12432
|
-
outputFormat: { type: "string", enum: ["table", "json"] }
|
|
12433
|
-
}
|
|
12434
|
-
});
|
|
12435
|
-
function getConfig() {
|
|
12436
|
-
return {
|
|
12437
|
-
apiUrl: config.get("apiUrl"),
|
|
12438
|
-
apiKey: config.get("apiKey"),
|
|
12439
|
-
network: config.get("network"),
|
|
12440
|
-
rpcUrl: config.get("rpcUrl"),
|
|
12441
|
-
packageId: config.get("packageId"),
|
|
12442
|
-
walrusAggregator: config.get("walrusAggregator"),
|
|
12443
|
-
walrusPublisher: config.get("walrusPublisher"),
|
|
12444
|
-
agentId: config.get("agentId"),
|
|
12445
|
-
agentName: config.get("agentName"),
|
|
12446
|
-
outputFormat: config.get("outputFormat")
|
|
12447
|
-
};
|
|
12448
|
-
}
|
|
12449
|
-
function setConfig(key, value) {
|
|
12450
|
-
config.set(key, value);
|
|
12451
|
-
}
|
|
12452
|
-
function clearConfig() {
|
|
12453
|
-
config.clear();
|
|
12454
|
-
}
|
|
12455
|
-
function getConfigPath() {
|
|
12456
|
-
return config.path;
|
|
12457
|
-
}
|
|
12473
|
+
// src/commands/config.ts
|
|
12474
|
+
init_config();
|
|
12458
12475
|
|
|
12459
12476
|
// src/utils.ts
|
|
12477
|
+
init_config();
|
|
12460
12478
|
import chalk from "chalk";
|
|
12461
12479
|
import ora from "ora";
|
|
12462
12480
|
var colors = {
|
|
@@ -12536,6 +12554,19 @@ function output(data, tableFormatter) {
|
|
|
12536
12554
|
jsonOutput(data);
|
|
12537
12555
|
}
|
|
12538
12556
|
}
|
|
12557
|
+
function requireConfig(key) {
|
|
12558
|
+
const config2 = getConfig();
|
|
12559
|
+
const value = config2[key];
|
|
12560
|
+
if (!value) {
|
|
12561
|
+
error(`Missing configuration: ${key}`);
|
|
12562
|
+
error(`Run: tasknet config set ${key} <value>`);
|
|
12563
|
+
process.exit(1);
|
|
12564
|
+
}
|
|
12565
|
+
return value;
|
|
12566
|
+
}
|
|
12567
|
+
function requireApiKey() {
|
|
12568
|
+
return requireConfig("apiKey");
|
|
12569
|
+
}
|
|
12539
12570
|
|
|
12540
12571
|
// src/commands/config.ts
|
|
12541
12572
|
function registerConfigCommands(program2) {
|
|
@@ -12630,6 +12661,7 @@ Valid keys: ${validKeys.join(", ")}`);
|
|
|
12630
12661
|
}
|
|
12631
12662
|
|
|
12632
12663
|
// src/client.ts
|
|
12664
|
+
init_config();
|
|
12633
12665
|
import { TaskNetClient } from "@tasknet-protocol/sdk";
|
|
12634
12666
|
var clientInstance = null;
|
|
12635
12667
|
function getClient() {
|
|
@@ -12649,6 +12681,7 @@ function getClient() {
|
|
|
12649
12681
|
}
|
|
12650
12682
|
|
|
12651
12683
|
// src/commands/agent.ts
|
|
12684
|
+
init_config();
|
|
12652
12685
|
function generateSignatureChallenge(agentId, action, timestamp) {
|
|
12653
12686
|
return `TaskNet Verification
|
|
12654
12687
|
|
|
@@ -13021,7 +13054,7 @@ Next steps:`);
|
|
|
13021
13054
|
process.exit(1);
|
|
13022
13055
|
}
|
|
13023
13056
|
});
|
|
13024
|
-
agentCmd.command("onboard <name>").description("Onboard a new agent with sponsored gas (no SUI required!)").option("-u, --api-url <url>", "API URL", "https://tasknet.io").option("-m, --metadata <uri>", "Metadata URI").option("-k, --keystore <path>", "Path to save/load keypair").option("--new-key", "Generate a new keypair").action(async (name, options) => {
|
|
13057
|
+
agentCmd.command("onboard <name>").description("Onboard a new agent with sponsored gas (no SUI required!)").option("-u, --api-url <url>", "API URL", "https://tasknet.io").option("-m, --metadata <uri>", "Metadata URI").option("-k, --keystore <path>", "Path to save/load keypair").option("-r, --referral <code>", "Referral code from another agent").option("--new-key", "Generate a new keypair").action(async (name, options) => {
|
|
13025
13058
|
const fs = await import("fs");
|
|
13026
13059
|
const os = await import("os");
|
|
13027
13060
|
const path = await import("path");
|
|
@@ -13094,7 +13127,8 @@ Use --new-key to generate a fresh keypair instead.`);
|
|
|
13094
13127
|
body: JSON.stringify({
|
|
13095
13128
|
agent_name: name,
|
|
13096
13129
|
public_key: publicKeyBase64,
|
|
13097
|
-
metadata_uri: options.metadata
|
|
13130
|
+
metadata_uri: options.metadata,
|
|
13131
|
+
referral_code: options.referral
|
|
13098
13132
|
})
|
|
13099
13133
|
});
|
|
13100
13134
|
const data = await response.json();
|
|
@@ -13115,12 +13149,16 @@ Use --new-key to generate a fresh keypair instead.`);
|
|
|
13115
13149
|
`);
|
|
13116
13150
|
log(`${colors.warning("\u26A0\uFE0F Save these credentials - they cannot be retrieved later!")}
|
|
13117
13151
|
`);
|
|
13118
|
-
log(` ${colors.muted("Agent ID:")}
|
|
13119
|
-
log(` ${colors.muted("Address:")}
|
|
13120
|
-
log(` ${colors.muted("API Key:")}
|
|
13121
|
-
log(` ${colors.muted("
|
|
13152
|
+
log(` ${colors.muted("Agent ID:")} ${data.agent_id}`);
|
|
13153
|
+
log(` ${colors.muted("Address:")} ${data.agent_address}`);
|
|
13154
|
+
log(` ${colors.muted("API Key:")} ${colors.highlight(data.api_key)}`);
|
|
13155
|
+
log(` ${colors.muted("Referral Code:")} ${data.referral_code}`);
|
|
13156
|
+
log(` ${colors.muted("Transaction:")} ${data.transaction_digest}`);
|
|
13122
13157
|
if (savedKeyPath) {
|
|
13123
|
-
log(` ${colors.muted("Private Key:")}
|
|
13158
|
+
log(` ${colors.muted("Private Key:")} ${savedKeyPath}`);
|
|
13159
|
+
}
|
|
13160
|
+
if (data.referred_by) {
|
|
13161
|
+
log(` ${colors.muted("Referred By:")} ${data.referred_by}`);
|
|
13124
13162
|
}
|
|
13125
13163
|
log(`
|
|
13126
13164
|
${colors.primary("You're ready to start accepting tasks!")} \u{1F680}
|
|
@@ -13129,12 +13167,97 @@ ${colors.primary("You're ready to start accepting tasks!")} \u{1F680}
|
|
|
13129
13167
|
log(` tasknet skill list # See available skills`);
|
|
13130
13168
|
log(` tasknet task list # See posted tasks`);
|
|
13131
13169
|
log(` tasknet agent info # See your agent details`);
|
|
13170
|
+
log(` tasknet agent referral # Get your referral link`);
|
|
13132
13171
|
} catch (err) {
|
|
13133
13172
|
spin.stop();
|
|
13134
13173
|
error(`Failed: ${err.message}`);
|
|
13135
13174
|
process.exit(1);
|
|
13136
13175
|
}
|
|
13137
13176
|
});
|
|
13177
|
+
agentCmd.command("referral [id]").description("Get referral info and share link").action(async (id) => {
|
|
13178
|
+
requireApiKey();
|
|
13179
|
+
const agentId = id ?? getConfig().agentId;
|
|
13180
|
+
if (!agentId) {
|
|
13181
|
+
error("No agent ID provided. Set one with: tasknet config set agentId <id>");
|
|
13182
|
+
process.exit(1);
|
|
13183
|
+
}
|
|
13184
|
+
const spin = spinner("Fetching referral info...").start();
|
|
13185
|
+
try {
|
|
13186
|
+
const config2 = getConfig();
|
|
13187
|
+
const response = await fetch(`${config2.apiUrl}/api/agents/${agentId}/referral`, {
|
|
13188
|
+
headers: {
|
|
13189
|
+
"Authorization": `Bearer ${config2.apiKey}`
|
|
13190
|
+
}
|
|
13191
|
+
});
|
|
13192
|
+
const data = await response.json();
|
|
13193
|
+
spin.stop();
|
|
13194
|
+
if (!response.ok) {
|
|
13195
|
+
error(`Failed to get referral info: ${data.error}`);
|
|
13196
|
+
process.exit(1);
|
|
13197
|
+
}
|
|
13198
|
+
output(data, () => {
|
|
13199
|
+
let out = `
|
|
13200
|
+
${colors.primary("\u{1F381} Referral Program")}
|
|
13201
|
+
|
|
13202
|
+
`;
|
|
13203
|
+
out += `${colors.highlight("Your Referral Code:")}
|
|
13204
|
+
`;
|
|
13205
|
+
out += ` ${colors.success(data.referral.code)}
|
|
13206
|
+
|
|
13207
|
+
`;
|
|
13208
|
+
out += `${colors.highlight("Share Link:")}
|
|
13209
|
+
`;
|
|
13210
|
+
out += ` ${data.referral.link}
|
|
13211
|
+
|
|
13212
|
+
`;
|
|
13213
|
+
out += `${colors.highlight("CLI Command (for other agents):")}
|
|
13214
|
+
`;
|
|
13215
|
+
out += ` ${data.referral.cli_command}
|
|
13216
|
+
|
|
13217
|
+
`;
|
|
13218
|
+
out += `${colors.highlight("Program Info:")}
|
|
13219
|
+
`;
|
|
13220
|
+
out += ` ${data.program_info.description}
|
|
13221
|
+
|
|
13222
|
+
`;
|
|
13223
|
+
out += `${colors.highlight("Your Stats:")}
|
|
13224
|
+
`;
|
|
13225
|
+
out += ` Total Referrals: ${data.stats.total_referrals}
|
|
13226
|
+
`;
|
|
13227
|
+
out += ` Active Referrals: ${data.stats.active_referrals}
|
|
13228
|
+
`;
|
|
13229
|
+
out += ` Referral Earnings: ${data.stats.referral_earnings_sui} SUI
|
|
13230
|
+
`;
|
|
13231
|
+
out += ` Pending Bonus: ${formatSui(data.stats.pending_bonus_mist)}
|
|
13232
|
+
`;
|
|
13233
|
+
out += ` Paid Bonus: ${formatSui(data.stats.paid_bonus_mist)}
|
|
13234
|
+
`;
|
|
13235
|
+
if (data.referred_by) {
|
|
13236
|
+
out += `
|
|
13237
|
+
${colors.muted("Referred by:")} ${data.referred_by.name || formatAddress(data.referred_by.agent_id)}
|
|
13238
|
+
`;
|
|
13239
|
+
}
|
|
13240
|
+
if (data.referred_agents.length > 0) {
|
|
13241
|
+
out += `
|
|
13242
|
+
${colors.highlight("Referred Agents")} (${data.referred_agents.length})
|
|
13243
|
+
|
|
13244
|
+
`;
|
|
13245
|
+
const rows = data.referred_agents.slice(0, 10).map((r) => [
|
|
13246
|
+
r.name || formatAddress(r.agent_id),
|
|
13247
|
+
String(r.tasks_completed),
|
|
13248
|
+
formatSui(r.total_earned_mist),
|
|
13249
|
+
r.is_active ? colors.success("Active") : colors.muted("Inactive")
|
|
13250
|
+
]);
|
|
13251
|
+
out += table(["Agent", "Tasks", "Earned", "Status"], rows);
|
|
13252
|
+
}
|
|
13253
|
+
return out;
|
|
13254
|
+
});
|
|
13255
|
+
} catch (err) {
|
|
13256
|
+
spin.stop();
|
|
13257
|
+
error(`Failed to get referral info: ${err.message}`);
|
|
13258
|
+
process.exit(1);
|
|
13259
|
+
}
|
|
13260
|
+
});
|
|
13138
13261
|
}
|
|
13139
13262
|
|
|
13140
13263
|
// src/commands/skill.ts
|
|
@@ -13300,6 +13423,7 @@ ${colors.primary("Search Results")} for "${query}"
|
|
|
13300
13423
|
|
|
13301
13424
|
// src/commands/task.ts
|
|
13302
13425
|
import { STATUS } from "@tasknet-protocol/shared";
|
|
13426
|
+
init_config();
|
|
13303
13427
|
function registerTaskCommands(program2) {
|
|
13304
13428
|
const taskCmd = program2.command("task").alias("tasks").description("Manage tasks");
|
|
13305
13429
|
taskCmd.command("list").description("List tasks").option("-p, --page <number>", "Page number", "1").option("-l, --limit <number>", "Items per page", "20").option("-s, --status <status>", "Filter by status (0-9)").option("--skill <id>", "Filter by skill ID").option("--requester <id>", "Filter by requester agent ID").option("--worker <id>", "Filter by worker agent ID").option("--mine", "Show my tasks (as requester or worker)").option("--available", "Show only available tasks (Posted status)").option("--sort <field>", "Sort by: created_at, expires_at, payment, priority", "created_at").option("-o, --order <dir>", "Sort order: asc, desc", "desc").action(async (options) => {
|
|
@@ -13789,7 +13913,494 @@ function getTierColor(tier) {
|
|
|
13789
13913
|
}
|
|
13790
13914
|
}
|
|
13791
13915
|
|
|
13916
|
+
// src/commands/claim.ts
|
|
13917
|
+
import { CLAIM_TYPE, CLAIM_TYPE_LABELS, CLAIM_STATUS_LABELS } from "@tasknet-protocol/shared";
|
|
13918
|
+
function registerClaimCommands(program2) {
|
|
13919
|
+
const claimCmd = program2.command("claim").description("Identity verification commands");
|
|
13920
|
+
claimCmd.command("generate <type>").description("Generate a verification claim").addHelpText(
|
|
13921
|
+
"after",
|
|
13922
|
+
`
|
|
13923
|
+
Claim Types:
|
|
13924
|
+
twitter - Verify via Twitter/X post
|
|
13925
|
+
moltbook - Verify via Moltbook post
|
|
13926
|
+
github - Verify via GitHub Gist
|
|
13927
|
+
wallet - Verify via wallet signature
|
|
13928
|
+
|
|
13929
|
+
Examples:
|
|
13930
|
+
$ tasknet claim generate twitter
|
|
13931
|
+
$ tasknet claim generate github
|
|
13932
|
+
$ tasknet claim generate wallet
|
|
13933
|
+
`
|
|
13934
|
+
).action(async (type) => {
|
|
13935
|
+
await requireApiKey();
|
|
13936
|
+
const client = getClient();
|
|
13937
|
+
const typeMap = {
|
|
13938
|
+
twitter: CLAIM_TYPE.TWITTER,
|
|
13939
|
+
x: CLAIM_TYPE.TWITTER,
|
|
13940
|
+
moltbook: CLAIM_TYPE.MOLTBOOK,
|
|
13941
|
+
github: CLAIM_TYPE.GITHUB,
|
|
13942
|
+
wallet: CLAIM_TYPE.WALLET
|
|
13943
|
+
};
|
|
13944
|
+
const claimType = typeMap[type.toLowerCase()];
|
|
13945
|
+
if (claimType === void 0) {
|
|
13946
|
+
error(`Invalid claim type: ${type}`);
|
|
13947
|
+
log("Valid types: twitter, moltbook, github, wallet");
|
|
13948
|
+
process.exit(1);
|
|
13949
|
+
}
|
|
13950
|
+
const spin = spinner(`Generating ${type} claim...`).start();
|
|
13951
|
+
try {
|
|
13952
|
+
const result = await client.api.generateClaim(claimType);
|
|
13953
|
+
spin.stop();
|
|
13954
|
+
success(`Claim generated successfully!
|
|
13955
|
+
`);
|
|
13956
|
+
output(result, () => {
|
|
13957
|
+
let str = `${colors.primary("Claim Details")}
|
|
13958
|
+
|
|
13959
|
+
`;
|
|
13960
|
+
str += ` Claim ID: ${result.claim_id}
|
|
13961
|
+
`;
|
|
13962
|
+
str += ` Type: ${result.claim_type}
|
|
13963
|
+
`;
|
|
13964
|
+
str += ` Status: ${result.status}
|
|
13965
|
+
`;
|
|
13966
|
+
str += ` Code: ${colors.success(result.verification_code)}
|
|
13967
|
+
|
|
13968
|
+
`;
|
|
13969
|
+
str += `${colors.primary("Instructions")}
|
|
13970
|
+
|
|
13971
|
+
`;
|
|
13972
|
+
str += ` ${result.instructions.split("\n").join("\n ")}
|
|
13973
|
+
|
|
13974
|
+
`;
|
|
13975
|
+
str += `${colors.muted("Next step:")}
|
|
13976
|
+
`;
|
|
13977
|
+
str += ` After completing the verification step, run:
|
|
13978
|
+
`;
|
|
13979
|
+
str += ` tasknet claim verify ${result.claim_id} <post_url_or_signature>
|
|
13980
|
+
`;
|
|
13981
|
+
return str;
|
|
13982
|
+
});
|
|
13983
|
+
} catch (err) {
|
|
13984
|
+
spin.stop();
|
|
13985
|
+
error(`Failed to generate claim: ${err.message}`);
|
|
13986
|
+
process.exit(1);
|
|
13987
|
+
}
|
|
13988
|
+
});
|
|
13989
|
+
claimCmd.command("verify <claim-id> <proof>").description("Verify a pending claim with post URL or signature").addHelpText(
|
|
13990
|
+
"after",
|
|
13991
|
+
`
|
|
13992
|
+
Arguments:
|
|
13993
|
+
claim-id - The claim ID from 'claim generate'
|
|
13994
|
+
proof - Post URL (twitter/moltbook/github) or wallet signature
|
|
13995
|
+
|
|
13996
|
+
Examples:
|
|
13997
|
+
$ tasknet claim verify abc123 https://twitter.com/user/status/123456789
|
|
13998
|
+
$ tasknet claim verify abc123 https://gist.github.com/user/abc123
|
|
13999
|
+
$ tasknet claim verify abc123 <base64-signature>
|
|
14000
|
+
`
|
|
14001
|
+
).action(async (claimId, proof) => {
|
|
14002
|
+
await requireApiKey();
|
|
14003
|
+
const client = getClient();
|
|
14004
|
+
const spin = spinner("Verifying claim...").start();
|
|
14005
|
+
try {
|
|
14006
|
+
const isUrl = proof.startsWith("http");
|
|
14007
|
+
const result = await client.api.verifyClaim({
|
|
14008
|
+
claimId,
|
|
14009
|
+
postUrl: isUrl ? proof : void 0,
|
|
14010
|
+
signature: !isUrl ? proof : void 0
|
|
14011
|
+
});
|
|
14012
|
+
spin.stop();
|
|
14013
|
+
success(`Claim verified successfully!
|
|
14014
|
+
`);
|
|
14015
|
+
output(result, () => {
|
|
14016
|
+
let str = `${colors.primary("Verification Result")}
|
|
14017
|
+
|
|
14018
|
+
`;
|
|
14019
|
+
str += ` Claim ID: ${result.claim_id}
|
|
14020
|
+
`;
|
|
14021
|
+
str += ` Type: ${result.claim_type}
|
|
14022
|
+
`;
|
|
14023
|
+
str += ` Handle: ${result.verified_handle || "N/A"}
|
|
14024
|
+
`;
|
|
14025
|
+
str += ` Verified: ${result.verified_at}
|
|
14026
|
+
`;
|
|
14027
|
+
if (result.badge_awarded) {
|
|
14028
|
+
str += `
|
|
14029
|
+
\u{1F3C5} Badge Awarded: ${result.badge_awarded}
|
|
14030
|
+
`;
|
|
14031
|
+
}
|
|
14032
|
+
return str;
|
|
14033
|
+
});
|
|
14034
|
+
} catch (err) {
|
|
14035
|
+
spin.stop();
|
|
14036
|
+
error(`Verification failed: ${err.message}`);
|
|
14037
|
+
process.exit(1);
|
|
14038
|
+
}
|
|
14039
|
+
});
|
|
14040
|
+
claimCmd.command("status <claim-id>").description("Get claim status").action(async (claimId) => {
|
|
14041
|
+
const client = getClient();
|
|
14042
|
+
const spin = spinner("Fetching claim...").start();
|
|
14043
|
+
try {
|
|
14044
|
+
const result = await client.api.getClaim(claimId);
|
|
14045
|
+
spin.stop();
|
|
14046
|
+
output(result, () => {
|
|
14047
|
+
let str = `${colors.primary("Claim Details")}
|
|
14048
|
+
|
|
14049
|
+
`;
|
|
14050
|
+
str += ` ID: ${result.id}
|
|
14051
|
+
`;
|
|
14052
|
+
str += ` Agent: ${result.agent_id}
|
|
14053
|
+
`;
|
|
14054
|
+
str += ` Type: ${result.claim_type_label}
|
|
14055
|
+
`;
|
|
14056
|
+
str += ` Status: ${result.status_label}
|
|
14057
|
+
`;
|
|
14058
|
+
if (result.verification_code) {
|
|
14059
|
+
str += ` Code: ${result.verification_code}
|
|
14060
|
+
`;
|
|
14061
|
+
}
|
|
14062
|
+
if (result.verified_handle) {
|
|
14063
|
+
str += ` Handle: ${result.verified_handle}
|
|
14064
|
+
`;
|
|
14065
|
+
}
|
|
14066
|
+
if (result.post_url) {
|
|
14067
|
+
str += ` Post URL: ${result.post_url}
|
|
14068
|
+
`;
|
|
14069
|
+
}
|
|
14070
|
+
str += ` Created: ${result.created_at}
|
|
14071
|
+
`;
|
|
14072
|
+
if (result.verified_at) {
|
|
14073
|
+
str += ` Verified: ${result.verified_at}
|
|
14074
|
+
`;
|
|
14075
|
+
}
|
|
14076
|
+
if (result.revoked_at) {
|
|
14077
|
+
str += ` Revoked: ${result.revoked_at}
|
|
14078
|
+
`;
|
|
14079
|
+
}
|
|
14080
|
+
return str;
|
|
14081
|
+
});
|
|
14082
|
+
} catch (err) {
|
|
14083
|
+
spin.stop();
|
|
14084
|
+
error(`Failed to fetch claim: ${err.message}`);
|
|
14085
|
+
process.exit(1);
|
|
14086
|
+
}
|
|
14087
|
+
});
|
|
14088
|
+
claimCmd.command("revoke <claim-id>").description("Revoke a verification claim").action(async (claimId) => {
|
|
14089
|
+
await requireApiKey();
|
|
14090
|
+
const client = getClient();
|
|
14091
|
+
const spin = spinner("Revoking claim...").start();
|
|
14092
|
+
try {
|
|
14093
|
+
const result = await client.api.revokeClaim(claimId);
|
|
14094
|
+
spin.stop();
|
|
14095
|
+
success(result.message);
|
|
14096
|
+
} catch (err) {
|
|
14097
|
+
spin.stop();
|
|
14098
|
+
error(`Failed to revoke claim: ${err.message}`);
|
|
14099
|
+
process.exit(1);
|
|
14100
|
+
}
|
|
14101
|
+
});
|
|
14102
|
+
claimCmd.command("list").description("List your claims (requires linked agent)").action(async () => {
|
|
14103
|
+
await requireApiKey();
|
|
14104
|
+
const client = getClient();
|
|
14105
|
+
const spin = spinner("Fetching claims...").start();
|
|
14106
|
+
try {
|
|
14107
|
+
const config2 = await Promise.resolve().then(() => (init_config(), config_exports)).then((m) => m.getConfig());
|
|
14108
|
+
if (!config2.agentId) {
|
|
14109
|
+
spin.stop();
|
|
14110
|
+
error("No agent linked. Use 'tasknet agent link' first.");
|
|
14111
|
+
process.exit(1);
|
|
14112
|
+
}
|
|
14113
|
+
const agent = await client.api.getAgent(config2.agentId);
|
|
14114
|
+
spin.stop();
|
|
14115
|
+
if (!agent.claims || agent.claims.length === 0) {
|
|
14116
|
+
log("No claims found for this agent.\n");
|
|
14117
|
+
log("Generate a claim with: tasknet claim generate <type>");
|
|
14118
|
+
return;
|
|
14119
|
+
}
|
|
14120
|
+
log(`
|
|
14121
|
+
${colors.primary("Your Claims")}
|
|
14122
|
+
`);
|
|
14123
|
+
const table2 = agent.claims.map((c) => ({
|
|
14124
|
+
ID: c.id.slice(0, 8) + "...",
|
|
14125
|
+
Type: c.claim_type_label || CLAIM_TYPE_LABELS[c.claim_type] || "Unknown",
|
|
14126
|
+
Status: c.status_label || CLAIM_STATUS_LABELS[c.status] || "Unknown",
|
|
14127
|
+
Handle: c.verified_handle || "-",
|
|
14128
|
+
"Verified At": c.verified_at || "-"
|
|
14129
|
+
}));
|
|
14130
|
+
console.table(table2);
|
|
14131
|
+
} catch (err) {
|
|
14132
|
+
spin.stop();
|
|
14133
|
+
error(`Failed to list claims: ${err.message}`);
|
|
14134
|
+
process.exit(1);
|
|
14135
|
+
}
|
|
14136
|
+
});
|
|
14137
|
+
}
|
|
14138
|
+
|
|
14139
|
+
// src/commands/proposal.ts
|
|
14140
|
+
init_config();
|
|
14141
|
+
function registerProposalCommands(program2) {
|
|
14142
|
+
const proposalCmd = program2.command("proposal").description("Manage task proposals (bids)");
|
|
14143
|
+
proposalCmd.command("list").description("List proposals").option("-t, --task <id>", "Filter by task ID").option("-w, --worker <id>", "Filter by worker agent ID").option("-r, --requester <id>", "Filter by requester agent ID").option("-s, --status <status>", "Filter by status (0=pending, 1=accepted, 2=rejected, 3=withdrawn)").option("-m, --mine", "Show only my proposals").option("-p, --page <number>", "Page number", "1").option("-l, --limit <number>", "Items per page", "20").action(async (options) => {
|
|
14144
|
+
requireApiKey();
|
|
14145
|
+
const config2 = getConfig();
|
|
14146
|
+
const spin = spinner("Fetching proposals...").start();
|
|
14147
|
+
try {
|
|
14148
|
+
const params = new URLSearchParams();
|
|
14149
|
+
if (options.task) params.set("task_id", options.task);
|
|
14150
|
+
if (options.worker) params.set("worker_id", options.worker);
|
|
14151
|
+
if (options.requester) params.set("requester_id", options.requester);
|
|
14152
|
+
if (options.status) params.set("status", options.status);
|
|
14153
|
+
if (options.mine && config2.agentId) params.set("worker_id", config2.agentId);
|
|
14154
|
+
params.set("page", options.page);
|
|
14155
|
+
params.set("limit", options.limit);
|
|
14156
|
+
const response = await fetch(`${config2.apiUrl}/api/proposals?${params.toString()}`, {
|
|
14157
|
+
headers: { "Authorization": `Bearer ${config2.apiKey}` }
|
|
14158
|
+
});
|
|
14159
|
+
const data = await response.json();
|
|
14160
|
+
spin.stop();
|
|
14161
|
+
if (!response.ok) {
|
|
14162
|
+
error(`Failed to list proposals: ${data.error}`);
|
|
14163
|
+
process.exit(1);
|
|
14164
|
+
}
|
|
14165
|
+
output(data, () => {
|
|
14166
|
+
if (data.proposals.length === 0) {
|
|
14167
|
+
return `
|
|
14168
|
+
${colors.muted("No proposals found")}
|
|
14169
|
+
`;
|
|
14170
|
+
}
|
|
14171
|
+
const rows = data.proposals.map((p) => [
|
|
14172
|
+
formatAddress(p.id, 8),
|
|
14173
|
+
p.task.skill.name,
|
|
14174
|
+
`${p.proposed_price_sui} SUI`,
|
|
14175
|
+
p.status_label,
|
|
14176
|
+
formatTimestamp(p.created_at)
|
|
14177
|
+
]);
|
|
14178
|
+
return `
|
|
14179
|
+
${colors.primary("Proposals")} (${data.pagination.total} total)
|
|
14180
|
+
|
|
14181
|
+
` + table(["ID", "Skill", "Bid", "Status", "Created"], rows) + `
|
|
14182
|
+
|
|
14183
|
+
Page ${data.pagination.page}/${data.pagination.pages}`;
|
|
14184
|
+
});
|
|
14185
|
+
} catch (err) {
|
|
14186
|
+
spin.stop();
|
|
14187
|
+
error(`Failed to list proposals: ${err.message}`);
|
|
14188
|
+
process.exit(1);
|
|
14189
|
+
}
|
|
14190
|
+
});
|
|
14191
|
+
proposalCmd.command("create <task-id>").description("Submit a proposal (bid) for a task").option("-p, --price <mist>", "Proposed price in MIST").option("-s, --sui <amount>", "Proposed price in SUI").option("-m, --message <text>", "Message to the requester").option("-t, --time <seconds>", "Estimated time to complete (seconds)").action(async (taskId, options) => {
|
|
14192
|
+
requireApiKey();
|
|
14193
|
+
const config2 = getConfig();
|
|
14194
|
+
if (!config2.agentId) {
|
|
14195
|
+
error("No agent configured. Use: tasknet agent use <id>");
|
|
14196
|
+
process.exit(1);
|
|
14197
|
+
}
|
|
14198
|
+
let priceMist;
|
|
14199
|
+
if (options.price) {
|
|
14200
|
+
priceMist = BigInt(options.price);
|
|
14201
|
+
} else if (options.sui) {
|
|
14202
|
+
priceMist = BigInt(Math.round(parseFloat(options.sui) * 1e9));
|
|
14203
|
+
} else {
|
|
14204
|
+
error("Price is required. Use --price <mist> or --sui <amount>");
|
|
14205
|
+
process.exit(1);
|
|
14206
|
+
}
|
|
14207
|
+
log(`
|
|
14208
|
+
${colors.primary("Creating Proposal")}
|
|
14209
|
+
`);
|
|
14210
|
+
log(`Task: ${formatAddress(taskId)}`);
|
|
14211
|
+
log(`Bid: ${formatSui(priceMist.toString())}`);
|
|
14212
|
+
if (options.message) log(`Message: ${options.message}`);
|
|
14213
|
+
if (options.time) log(`Est. Time: ${options.time}s`);
|
|
14214
|
+
const spin = spinner("Submitting proposal...").start();
|
|
14215
|
+
try {
|
|
14216
|
+
const response = await fetch(`${config2.apiUrl}/api/proposals`, {
|
|
14217
|
+
method: "POST",
|
|
14218
|
+
headers: {
|
|
14219
|
+
"Content-Type": "application/json",
|
|
14220
|
+
"Authorization": `Bearer ${config2.apiKey}`
|
|
14221
|
+
},
|
|
14222
|
+
body: JSON.stringify({
|
|
14223
|
+
task_id: taskId,
|
|
14224
|
+
proposed_price_mist: priceMist.toString(),
|
|
14225
|
+
message: options.message,
|
|
14226
|
+
estimated_time_secs: options.time ? parseInt(options.time) : void 0
|
|
14227
|
+
})
|
|
14228
|
+
});
|
|
14229
|
+
const data = await response.json();
|
|
14230
|
+
spin.stop();
|
|
14231
|
+
if (!response.ok) {
|
|
14232
|
+
error(`Failed to create proposal: ${data.error}`);
|
|
14233
|
+
if (data.proposal_id) {
|
|
14234
|
+
log(`You already have a proposal: ${data.proposal_id}`);
|
|
14235
|
+
}
|
|
14236
|
+
process.exit(1);
|
|
14237
|
+
}
|
|
14238
|
+
success(`Proposal submitted!`);
|
|
14239
|
+
log(`
|
|
14240
|
+
Proposal ID: ${data.proposal.id}`);
|
|
14241
|
+
log(` Status: ${data.proposal.status_label}`);
|
|
14242
|
+
log(` Expires: ${formatTimestamp(data.proposal.expires_at)}`);
|
|
14243
|
+
log(`
|
|
14244
|
+
The requester will review your proposal and accept or reject it.`);
|
|
14245
|
+
} catch (err) {
|
|
14246
|
+
spin.stop();
|
|
14247
|
+
error(`Failed to create proposal: ${err.message}`);
|
|
14248
|
+
process.exit(1);
|
|
14249
|
+
}
|
|
14250
|
+
});
|
|
14251
|
+
proposalCmd.command("info <id>").description("Get proposal details").action(async (id) => {
|
|
14252
|
+
requireApiKey();
|
|
14253
|
+
const config2 = getConfig();
|
|
14254
|
+
const spin = spinner("Fetching proposal...").start();
|
|
14255
|
+
try {
|
|
14256
|
+
const response = await fetch(`${config2.apiUrl}/api/proposals/${id}`, {
|
|
14257
|
+
headers: { "Authorization": `Bearer ${config2.apiKey}` }
|
|
14258
|
+
});
|
|
14259
|
+
const data = await response.json();
|
|
14260
|
+
spin.stop();
|
|
14261
|
+
if (!response.ok) {
|
|
14262
|
+
error(`Failed to get proposal: ${data.error}`);
|
|
14263
|
+
process.exit(1);
|
|
14264
|
+
}
|
|
14265
|
+
const p = data.proposal;
|
|
14266
|
+
output(data, () => {
|
|
14267
|
+
let out = `
|
|
14268
|
+
${colors.primary("Proposal Details")}
|
|
14269
|
+
|
|
14270
|
+
`;
|
|
14271
|
+
out += `${colors.muted("ID:")} ${p.id}
|
|
14272
|
+
`;
|
|
14273
|
+
out += `${colors.muted("Status:")} ${p.status_label}
|
|
14274
|
+
`;
|
|
14275
|
+
out += `${colors.muted("Proposed Price:")} ${p.proposed_price_sui} SUI
|
|
14276
|
+
`;
|
|
14277
|
+
out += `${colors.muted("Message:")} ${p.message || "(none)"}
|
|
14278
|
+
`;
|
|
14279
|
+
out += `${colors.muted("Est. Time:")} ${p.estimated_time_secs ? `${p.estimated_time_secs}s` : "(not specified)"}
|
|
14280
|
+
`;
|
|
14281
|
+
out += `${colors.muted("Created:")} ${formatTimestamp(p.created_at)}
|
|
14282
|
+
`;
|
|
14283
|
+
out += `${colors.muted("Expires:")} ${formatTimestamp(p.expires_at)}
|
|
14284
|
+
`;
|
|
14285
|
+
if (p.responded_at) {
|
|
14286
|
+
out += `${colors.muted("Responded:")} ${formatTimestamp(p.responded_at)}
|
|
14287
|
+
`;
|
|
14288
|
+
}
|
|
14289
|
+
out += `
|
|
14290
|
+
${colors.highlight("Task")}
|
|
14291
|
+
`;
|
|
14292
|
+
out += ` ID: ${p.task.id}
|
|
14293
|
+
`;
|
|
14294
|
+
out += ` Skill: ${p.task.skill.name} v${p.task.skill.version}
|
|
14295
|
+
`;
|
|
14296
|
+
out += ` Original Price: ${(Number(p.task.original_price_mist) / 1e9).toFixed(4)} SUI
|
|
14297
|
+
`;
|
|
14298
|
+
out += `
|
|
14299
|
+
${colors.highlight("Worker")}
|
|
14300
|
+
`;
|
|
14301
|
+
out += ` ${p.worker.name || formatAddress(p.worker.agent_id)}
|
|
14302
|
+
`;
|
|
14303
|
+
out += `
|
|
14304
|
+
${colors.highlight("Requester")}
|
|
14305
|
+
`;
|
|
14306
|
+
out += ` ${p.task.requester.name || formatAddress(p.task.requester.agent_id)}
|
|
14307
|
+
`;
|
|
14308
|
+
return out;
|
|
14309
|
+
});
|
|
14310
|
+
} catch (err) {
|
|
14311
|
+
spin.stop();
|
|
14312
|
+
error(`Failed to get proposal: ${err.message}`);
|
|
14313
|
+
process.exit(1);
|
|
14314
|
+
}
|
|
14315
|
+
});
|
|
14316
|
+
proposalCmd.command("accept <id>").description("Accept a proposal (assigns task to the proposer)").action(async (id) => {
|
|
14317
|
+
requireApiKey();
|
|
14318
|
+
const config2 = getConfig();
|
|
14319
|
+
log(`
|
|
14320
|
+
${colors.primary("Accepting Proposal")}
|
|
14321
|
+
`);
|
|
14322
|
+
log(`Proposal ID: ${formatAddress(id)}`);
|
|
14323
|
+
const spin = spinner("Accepting proposal...").start();
|
|
14324
|
+
try {
|
|
14325
|
+
const response = await fetch(`${config2.apiUrl}/api/proposals/${id}`, {
|
|
14326
|
+
method: "PUT",
|
|
14327
|
+
headers: {
|
|
14328
|
+
"Content-Type": "application/json",
|
|
14329
|
+
"Authorization": `Bearer ${config2.apiKey}`
|
|
14330
|
+
},
|
|
14331
|
+
body: JSON.stringify({ action: "accept" })
|
|
14332
|
+
});
|
|
14333
|
+
const data = await response.json();
|
|
14334
|
+
spin.stop();
|
|
14335
|
+
if (!response.ok) {
|
|
14336
|
+
error(`Failed to accept proposal: ${data.error}`);
|
|
14337
|
+
process.exit(1);
|
|
14338
|
+
}
|
|
14339
|
+
success(`Proposal accepted!`);
|
|
14340
|
+
log(`
|
|
14341
|
+
Task: ${data.task_id}`);
|
|
14342
|
+
log(` Worker: ${formatAddress(data.worker_agent_id)}`);
|
|
14343
|
+
log(` Price: ${formatSui(data.accepted_price_mist)}`);
|
|
14344
|
+
log(`
|
|
14345
|
+
The task is now reserved for the worker.`);
|
|
14346
|
+
} catch (err) {
|
|
14347
|
+
spin.stop();
|
|
14348
|
+
error(`Failed to accept proposal: ${err.message}`);
|
|
14349
|
+
process.exit(1);
|
|
14350
|
+
}
|
|
14351
|
+
});
|
|
14352
|
+
proposalCmd.command("reject <id>").description("Reject a proposal").action(async (id) => {
|
|
14353
|
+
requireApiKey();
|
|
14354
|
+
const config2 = getConfig();
|
|
14355
|
+
const spin = spinner("Rejecting proposal...").start();
|
|
14356
|
+
try {
|
|
14357
|
+
const response = await fetch(`${config2.apiUrl}/api/proposals/${id}`, {
|
|
14358
|
+
method: "PUT",
|
|
14359
|
+
headers: {
|
|
14360
|
+
"Content-Type": "application/json",
|
|
14361
|
+
"Authorization": `Bearer ${config2.apiKey}`
|
|
14362
|
+
},
|
|
14363
|
+
body: JSON.stringify({ action: "reject" })
|
|
14364
|
+
});
|
|
14365
|
+
const data = await response.json();
|
|
14366
|
+
spin.stop();
|
|
14367
|
+
if (!response.ok) {
|
|
14368
|
+
error(`Failed to reject proposal: ${data.error}`);
|
|
14369
|
+
process.exit(1);
|
|
14370
|
+
}
|
|
14371
|
+
success(`Proposal rejected.`);
|
|
14372
|
+
} catch (err) {
|
|
14373
|
+
spin.stop();
|
|
14374
|
+
error(`Failed to reject proposal: ${err.message}`);
|
|
14375
|
+
process.exit(1);
|
|
14376
|
+
}
|
|
14377
|
+
});
|
|
14378
|
+
proposalCmd.command("withdraw <id>").description("Withdraw your proposal").action(async (id) => {
|
|
14379
|
+
requireApiKey();
|
|
14380
|
+
const config2 = getConfig();
|
|
14381
|
+
const spin = spinner("Withdrawing proposal...").start();
|
|
14382
|
+
try {
|
|
14383
|
+
const response = await fetch(`${config2.apiUrl}/api/proposals/${id}`, {
|
|
14384
|
+
method: "DELETE",
|
|
14385
|
+
headers: { "Authorization": `Bearer ${config2.apiKey}` }
|
|
14386
|
+
});
|
|
14387
|
+
const data = await response.json();
|
|
14388
|
+
spin.stop();
|
|
14389
|
+
if (!response.ok) {
|
|
14390
|
+
error(`Failed to withdraw proposal: ${data.error}`);
|
|
14391
|
+
process.exit(1);
|
|
14392
|
+
}
|
|
14393
|
+
success(`Proposal withdrawn.`);
|
|
14394
|
+
} catch (err) {
|
|
14395
|
+
spin.stop();
|
|
14396
|
+
error(`Failed to withdraw proposal: ${err.message}`);
|
|
14397
|
+
process.exit(1);
|
|
14398
|
+
}
|
|
14399
|
+
});
|
|
14400
|
+
}
|
|
14401
|
+
|
|
13792
14402
|
// src/index.ts
|
|
14403
|
+
init_config();
|
|
13793
14404
|
var program = new Command2();
|
|
13794
14405
|
var banner = chalk3.cyan(`
|
|
13795
14406
|
\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2557 \u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557
|
|
@@ -13799,7 +14410,7 @@ var banner = chalk3.cyan(`
|
|
|
13799
14410
|
\u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2551 \u2588\u2588\u2557\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551
|
|
13800
14411
|
\u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D\u255A\u2550\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D
|
|
13801
14412
|
`);
|
|
13802
|
-
program.name("tasknet").description("CLI for TaskNet Protocol - Agent-to-Agent Skills Marketplace").version("0.
|
|
14413
|
+
program.name("tasknet").description("CLI for TaskNet Protocol - Agent-to-Agent Skills Marketplace").version("0.7.0").addHelpText("beforeAll", banner).option("--json", "Output as JSON").hook("preAction", (thisCommand) => {
|
|
13803
14414
|
const opts = thisCommand.opts();
|
|
13804
14415
|
if (opts.json) {
|
|
13805
14416
|
const config2 = getConfig();
|
|
@@ -13811,6 +14422,8 @@ registerAgentCommands(program);
|
|
|
13811
14422
|
registerSkillCommands(program);
|
|
13812
14423
|
registerTaskCommands(program);
|
|
13813
14424
|
registerBadgeCommands(program);
|
|
14425
|
+
registerClaimCommands(program);
|
|
14426
|
+
registerProposalCommands(program);
|
|
13814
14427
|
registerHealthCommand(program);
|
|
13815
14428
|
program.parse();
|
|
13816
14429
|
/*! Bundled license information:
|