@itpay/cli 0.1.9 → 0.2.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 +10 -1
- package/bin/itp +54 -4485
- package/docs/agent/buyer/cart-checkout.json +20 -11
- package/docs/agent/buyer/catalog-search.json +23 -16
- package/docs/agent/buyer/human-claim-ui.json +2 -0
- package/docs/agent/buyer/payment-qr.json +1 -0
- package/docs/agent/buyer/payment-wait.json +5 -1
- package/docs/agent/buyer/qr-refresh.json +2 -0
- package/docs/agent/buyer/quickstart.json +11 -2
- package/docs/agent/buyer/recovery.json +3 -0
- package/docs/agent/buyer/secure-delivery.json +2 -0
- package/docs/agent/buyer/vault-agent-read.json +4 -0
- package/install.ps1 +15 -3
- package/install.sh +16 -3
- package/lib/buyer.js +1675 -0
- package/lib/docs.js +200 -0
- package/lib/env.js +713 -0
- package/lib/http.js +151 -0
- package/lib/ops.js +135 -0
- package/lib/render-human.js +463 -0
- package/lib/runtime.js +1532 -0
- package/package.json +2 -1
- package/skills/itpay-buyer/SKILL.md +12 -3
package/lib/docs.js
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { CLI_DIR, PACKAGE_ROOT, cliCommand, output, positionalArgs, readJSON } from "./env.js";
|
|
4
|
+
|
|
5
|
+
async function docs(command, rest = [], flags = {}) {
|
|
6
|
+
const role = normalizeDocsRole(flags.role || "buyer");
|
|
7
|
+
if (command === "list" || !command) {
|
|
8
|
+
const docsList = listAgentDocs(role);
|
|
9
|
+
output({
|
|
10
|
+
schema_version: "itp.agent_doc_index.v1",
|
|
11
|
+
role,
|
|
12
|
+
topics: docsList.map((doc) => ({
|
|
13
|
+
topic: doc.topic,
|
|
14
|
+
title: doc.title,
|
|
15
|
+
purpose: doc.purpose,
|
|
16
|
+
command: cliCommand("docs", "show", doc.topic, "--role", role, "--json"),
|
|
17
|
+
next_docs: Array.isArray(doc.next_docs) ? doc.next_docs.map((next) => next.topic).filter(Boolean) : []
|
|
18
|
+
})),
|
|
19
|
+
start_here: cliCommand("docs", "show", "quickstart", "--role", role, "--json"),
|
|
20
|
+
search_command: cliCommand("docs", "search", "<question>", "--role", role, "--json")
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
if (command === "show" || command === "read") {
|
|
25
|
+
const topic = flags.topic || positionalArgs(rest)[0] || "quickstart";
|
|
26
|
+
output(loadAgentDoc(role, topic));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
if (command === "search") {
|
|
30
|
+
const query = String(flags.query || flags.q || positionalArgs(rest).join(" ")).trim();
|
|
31
|
+
if (!query) throw new Error("docs search query is required");
|
|
32
|
+
const matches = searchAgentDocs(role, query);
|
|
33
|
+
output({
|
|
34
|
+
schema_version: "itp.agent_doc_search.v1",
|
|
35
|
+
role,
|
|
36
|
+
query,
|
|
37
|
+
matches,
|
|
38
|
+
fallback: matches.length ? null : {
|
|
39
|
+
topic: "quickstart",
|
|
40
|
+
command: cliCommand("docs", "show", "quickstart", "--role", role, "--json")
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
throw new Error(`unknown docs command: ${command}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function normalizeDocsRole(role) {
|
|
49
|
+
const normalized = String(role || "buyer").trim().toLowerCase();
|
|
50
|
+
if (normalized === "buyer" || normalized === "itpay-buyer") return "buyer";
|
|
51
|
+
throw new Error(`unsupported docs role: ${role}`);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function listAgentDocs(role) {
|
|
55
|
+
const docsDir = resolveDocsDir(role);
|
|
56
|
+
return fs.readdirSync(docsDir)
|
|
57
|
+
.filter((name) => name.endsWith(".json"))
|
|
58
|
+
.map((name) => loadAgentDoc(role, name.replace(/\.json$/, "")))
|
|
59
|
+
.sort((a, b) => docTopicOrder(a.topic) - docTopicOrder(b.topic) || a.topic.localeCompare(b.topic));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function loadAgentDoc(role, topic) {
|
|
63
|
+
const normalizedTopic = normalizeDocTopic(topic);
|
|
64
|
+
const file = path.join(resolveDocsDir(role), `${normalizedTopic}.json`);
|
|
65
|
+
if (!fs.existsSync(file)) {
|
|
66
|
+
throw new Error(`agent docs topic not found: ${normalizedTopic}`);
|
|
67
|
+
}
|
|
68
|
+
const doc = readJSON(file, null);
|
|
69
|
+
if (!doc || doc.role !== role || doc.topic !== normalizedTopic) {
|
|
70
|
+
throw new Error(`invalid agent docs topic: ${normalizedTopic}`);
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
...doc,
|
|
74
|
+
source: {
|
|
75
|
+
packaged_path: file,
|
|
76
|
+
command: cliCommand("docs", "show", normalizedTopic, "--role", role, "--json")
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function searchAgentDocs(role, query) {
|
|
82
|
+
const rawQuery = String(query).toLowerCase();
|
|
83
|
+
const terms = rawQuery.split(/\s+/).filter(Boolean);
|
|
84
|
+
return listAgentDocs(role)
|
|
85
|
+
.map((doc) => {
|
|
86
|
+
const docTerms = (doc.search_terms || []).map((term) => String(term).toLowerCase()).filter(Boolean);
|
|
87
|
+
const haystack = [
|
|
88
|
+
doc.topic,
|
|
89
|
+
doc.title,
|
|
90
|
+
doc.purpose,
|
|
91
|
+
...(doc.when_to_use || []),
|
|
92
|
+
...(doc.agent_rules || []),
|
|
93
|
+
...(doc.forbidden || []),
|
|
94
|
+
...docTerms
|
|
95
|
+
].join(" ").toLowerCase();
|
|
96
|
+
const score = terms.reduce((sum, term) => sum + (haystack.includes(term) ? 1 : 0), 0) +
|
|
97
|
+
docTerms.reduce((sum, term) => sum + (rawQuery.includes(term) ? 1 : 0), 0);
|
|
98
|
+
return { doc, score };
|
|
99
|
+
})
|
|
100
|
+
.filter((entry) => entry.score > 0)
|
|
101
|
+
.sort((a, b) => b.score - a.score || docTopicOrder(a.doc.topic) - docTopicOrder(b.doc.topic))
|
|
102
|
+
.slice(0, 5)
|
|
103
|
+
.map((entry) => ({
|
|
104
|
+
topic: entry.doc.topic,
|
|
105
|
+
title: entry.doc.title,
|
|
106
|
+
purpose: entry.doc.purpose,
|
|
107
|
+
score: entry.score,
|
|
108
|
+
command: cliCommand("docs", "show", entry.doc.topic, "--role", role, "--json"),
|
|
109
|
+
next_docs: Array.isArray(entry.doc.next_docs) ? entry.doc.next_docs.map((next) => next.topic).filter(Boolean) : []
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resolveDocsDir(role) {
|
|
114
|
+
const candidates = [
|
|
115
|
+
process.env.ITPAY_CLI_DOCS_DIR,
|
|
116
|
+
path.join(PACKAGE_ROOT, "docs", "agent", role),
|
|
117
|
+
path.join(path.dirname(CLI_DIR), "share", "itpay_cli", "docs", "agent", role),
|
|
118
|
+
path.join(process.cwd(), "docs", "agent", role)
|
|
119
|
+
].filter(Boolean);
|
|
120
|
+
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
|
121
|
+
if (!found) {
|
|
122
|
+
throw new Error(`ItPay agent docs not found for role ${role}. Checked: ${candidates.join(", ")}`);
|
|
123
|
+
}
|
|
124
|
+
return found;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function normalizeDocTopic(topic) {
|
|
128
|
+
return String(topic || "").trim().toLowerCase().replaceAll("_", "-");
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function docTopicOrder(topic) {
|
|
132
|
+
const order = [
|
|
133
|
+
"quickstart",
|
|
134
|
+
"catalog-search",
|
|
135
|
+
"product-recommendation",
|
|
136
|
+
"cart-checkout",
|
|
137
|
+
"payment-qr",
|
|
138
|
+
"payment-wait",
|
|
139
|
+
"qr-refresh",
|
|
140
|
+
"secure-delivery",
|
|
141
|
+
"human-claim-ui",
|
|
142
|
+
"account-portal",
|
|
143
|
+
"vault-agent-read",
|
|
144
|
+
"recovery",
|
|
145
|
+
"safety-policy"
|
|
146
|
+
];
|
|
147
|
+
const index = order.indexOf(topic);
|
|
148
|
+
return index === -1 ? 999 : index;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
async function skill(command, flags) {
|
|
152
|
+
const role = normalizeSkillRole(flags.role || flags.skill || "buyer");
|
|
153
|
+
const skillPath = resolveSkillPath(role);
|
|
154
|
+
if (!command || command === "show" || command === "read") {
|
|
155
|
+
const content = fs.readFileSync(skillPath, "utf8");
|
|
156
|
+
if (flags.json) {
|
|
157
|
+
output({ skill: "itpay-buyer", role, path: skillPath, content });
|
|
158
|
+
} else {
|
|
159
|
+
process.stdout.write(content.endsWith("\n") ? content : `${content}\n`);
|
|
160
|
+
}
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
if (command === "path") {
|
|
164
|
+
if (flags.json) {
|
|
165
|
+
output({ skill: "itpay-buyer", role, path: skillPath });
|
|
166
|
+
} else {
|
|
167
|
+
process.stdout.write(`${skillPath}\n`);
|
|
168
|
+
}
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
throw new Error(`unknown skill command: ${command}`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function normalizeSkillRole(role) {
|
|
175
|
+
const normalized = String(role || "buyer").trim().toLowerCase();
|
|
176
|
+
if (normalized === "buyer" || normalized === "itpay-buyer") return "buyer";
|
|
177
|
+
if (normalized === "merchant" || normalized === "itpay-merchant") {
|
|
178
|
+
throw new Error("merchant skill is not packaged yet; use --role buyer for current external-agent tests");
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`unsupported skill role: ${role}`);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function resolveSkillPath(role = "buyer") {
|
|
184
|
+
const skillDirName = "itpay-buyer";
|
|
185
|
+
const envPath = process.env.ITPAY_BUYER_SKILL_PATH;
|
|
186
|
+
const candidates = [
|
|
187
|
+
envPath,
|
|
188
|
+
process.env.ITPAY_CLI_SKILL_PATH,
|
|
189
|
+
path.join(PACKAGE_ROOT, "skills", skillDirName, "SKILL.md"),
|
|
190
|
+
path.join(path.dirname(CLI_DIR), "share", "itpay_cli", "skills", skillDirName, "SKILL.md"),
|
|
191
|
+
path.join(process.cwd(), "skills", skillDirName, "SKILL.md")
|
|
192
|
+
].filter(Boolean);
|
|
193
|
+
const found = candidates.find((candidate) => fs.existsSync(candidate));
|
|
194
|
+
if (!found) {
|
|
195
|
+
throw new Error(`ItPay skill file not found for role ${role}. Checked: ${candidates.join(", ")}`);
|
|
196
|
+
}
|
|
197
|
+
return found;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export { docs, skill };
|