@buygent/cli 0.3.10 → 0.4.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/cli.js +188 -178
- package/dist/extension/content/amazon.js +280 -0
- package/dist/extension/manifest.json +15 -3
- package/dist/native-host.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
// extension/protocol.ts
|
|
3
|
+
var BUYGENT_PROTOCOL_VERSION = 1;
|
|
4
|
+
var BUYGENT_NATIVE_HOST_NAME = "com.buygent.host";
|
|
5
|
+
var NATIVE_HOST_SOCKET_FILENAME = `${BUYGENT_NATIVE_HOST_NAME}.sock`;
|
|
6
|
+
var createEnvelope = (type, payload, options) => ({
|
|
7
|
+
protocolVersion: BUYGENT_PROTOCOL_VERSION,
|
|
8
|
+
type,
|
|
9
|
+
messageId: options.messageId,
|
|
10
|
+
...options.requestId ? { requestId: options.requestId } : {},
|
|
11
|
+
sentAt: options.sentAt ?? new Date().toISOString(),
|
|
12
|
+
source: options.source,
|
|
13
|
+
payload
|
|
14
|
+
});
|
|
15
|
+
var createErrorEnvelope = (payload, options) => createEnvelope("error", payload, options);
|
|
16
|
+
var isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
17
|
+
var decodeEnvelope = (raw, options = {}) => {
|
|
18
|
+
const fallback = {
|
|
19
|
+
source: options.source ?? "native-host",
|
|
20
|
+
messageId: options.messageId ?? `protocol_${Date.now()}`,
|
|
21
|
+
requestId: options.requestId,
|
|
22
|
+
sentAt: options.sentAt
|
|
23
|
+
};
|
|
24
|
+
if (!isRecord(raw)) {
|
|
25
|
+
return {
|
|
26
|
+
ok: false,
|
|
27
|
+
error: createErrorEnvelope({
|
|
28
|
+
code: "invalid_envelope",
|
|
29
|
+
message: "Expected a JSON object envelope."
|
|
30
|
+
}, fallback)
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
if (raw.protocolVersion !== BUYGENT_PROTOCOL_VERSION) {
|
|
34
|
+
return {
|
|
35
|
+
ok: false,
|
|
36
|
+
error: createErrorEnvelope({
|
|
37
|
+
code: "unsupported_protocol_version",
|
|
38
|
+
message: `Unsupported protocolVersion: ${String(raw.protocolVersion ?? "missing")}`,
|
|
39
|
+
details: {
|
|
40
|
+
supportedProtocolVersion: BUYGENT_PROTOCOL_VERSION
|
|
41
|
+
}
|
|
42
|
+
}, fallback)
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (typeof raw.type !== "string" || typeof raw.messageId !== "string" || typeof raw.sentAt !== "string" || typeof raw.source !== "string") {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
error: createErrorEnvelope({
|
|
49
|
+
code: "invalid_envelope",
|
|
50
|
+
message: "Envelope must include type, messageId, sentAt, and source fields."
|
|
51
|
+
}, fallback)
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
ok: true,
|
|
56
|
+
envelope: raw
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
var TERMINAL_ORDER_STATES = new Set(["aborted", "completed_dry_run"]);
|
|
60
|
+
|
|
61
|
+
// extension/amazon-selector-config.ts
|
|
62
|
+
var BUNDLED_AMAZON_SELECTOR_CONFIG = {
|
|
63
|
+
version: "amazon.v1",
|
|
64
|
+
platform: "amazon",
|
|
65
|
+
generatedAt: "2026-05-08T00:00:00.000Z",
|
|
66
|
+
selectors: {
|
|
67
|
+
loginIndicators: [
|
|
68
|
+
"#nav-link-accountList[data-nav-ref='nav_youraccount_btn']",
|
|
69
|
+
"#nav-item-signout",
|
|
70
|
+
"span.nav-line-1:not(:empty)"
|
|
71
|
+
],
|
|
72
|
+
loggedOutIndicators: [
|
|
73
|
+
"#nav-link-accountList span:first-child",
|
|
74
|
+
"a[href*='signin']"
|
|
75
|
+
],
|
|
76
|
+
productTitle: [
|
|
77
|
+
"#productTitle",
|
|
78
|
+
"#title span",
|
|
79
|
+
"h1.product-title-word-break"
|
|
80
|
+
],
|
|
81
|
+
productPrice: [
|
|
82
|
+
".a-price .a-offscreen",
|
|
83
|
+
"#priceblock_ourprice",
|
|
84
|
+
"#priceblock_dealprice",
|
|
85
|
+
"span.a-price-whole",
|
|
86
|
+
"#corePrice_feature_div .a-offscreen"
|
|
87
|
+
],
|
|
88
|
+
addToCartButton: [
|
|
89
|
+
"#add-to-cart-button",
|
|
90
|
+
"#add-to-cart-button-ubb",
|
|
91
|
+
"input[name='submit.add-to-cart']",
|
|
92
|
+
"#addToCart .a-button-text",
|
|
93
|
+
"span.a-button-text:has-text(Add to Cart)",
|
|
94
|
+
"#addToCart input[type='submit']"
|
|
95
|
+
],
|
|
96
|
+
buyNowButton: [
|
|
97
|
+
"#buy-now-button",
|
|
98
|
+
"#submitOrderButtonId",
|
|
99
|
+
"#buy-now-button .a-button-text"
|
|
100
|
+
],
|
|
101
|
+
optionSelectors: [
|
|
102
|
+
"#native_dropdown_selected_size_name",
|
|
103
|
+
"#native_dropdown_selected_color_name",
|
|
104
|
+
"#variation_size_name select",
|
|
105
|
+
"#variation_color_name ul li",
|
|
106
|
+
".a-dropdown-container select"
|
|
107
|
+
],
|
|
108
|
+
cartItems: [
|
|
109
|
+
".sc-list-item",
|
|
110
|
+
"div[data-item-id]",
|
|
111
|
+
".sc-list-body .sc-action-links"
|
|
112
|
+
],
|
|
113
|
+
cartTotal: [
|
|
114
|
+
"#sc-subtotal-amount-activecart",
|
|
115
|
+
"#sc-subtotal-label-activecart + .a-text-bold"
|
|
116
|
+
],
|
|
117
|
+
proceedToCheckout: [
|
|
118
|
+
"#sc-buy-box-ptc-button",
|
|
119
|
+
"input[name='proceedToRetailCheckout']",
|
|
120
|
+
"#hlb-ptc-btn-native"
|
|
121
|
+
],
|
|
122
|
+
placeOrder: [
|
|
123
|
+
"#submitOrderButtonId",
|
|
124
|
+
"#placeYourOrder input",
|
|
125
|
+
"#bottomSubmitOrderButtonId"
|
|
126
|
+
],
|
|
127
|
+
orderConfirmation: [
|
|
128
|
+
"#thank-you-page",
|
|
129
|
+
".a-alert-heading",
|
|
130
|
+
"#confirmationPage",
|
|
131
|
+
"h4.a-alert-heading"
|
|
132
|
+
],
|
|
133
|
+
searchInput: [
|
|
134
|
+
"#twotabsearchtextbox",
|
|
135
|
+
"#nav-search-keywords"
|
|
136
|
+
],
|
|
137
|
+
searchButton: [
|
|
138
|
+
"#nav-search-submit-button",
|
|
139
|
+
"input[type='submit'][value='Go']"
|
|
140
|
+
],
|
|
141
|
+
searchResults: [
|
|
142
|
+
"div[data-component-type='s-search-result']",
|
|
143
|
+
".s-result-item[data-asin]"
|
|
144
|
+
]
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
// extension/content/amazon-probe.ts
|
|
149
|
+
var detectPageType = (url) => {
|
|
150
|
+
if (/\/s[?/]/.test(url) || /\/s\?k=/.test(url))
|
|
151
|
+
return "search";
|
|
152
|
+
if (/\/dp\/|\/gp\/product\//.test(url))
|
|
153
|
+
return "product";
|
|
154
|
+
if (/\/cart|\/gp\/cart/.test(url))
|
|
155
|
+
return "cart";
|
|
156
|
+
if (/\/checkout|\/buy\//.test(url) || /\/gp\/buy/.test(url))
|
|
157
|
+
return "checkout";
|
|
158
|
+
if (/thankyou|order-confirmation|gp\/buy\/thankyou/.test(url))
|
|
159
|
+
return "orderConfirmation";
|
|
160
|
+
return "other";
|
|
161
|
+
};
|
|
162
|
+
var querySelector = (doc, selectors) => {
|
|
163
|
+
for (const sel of selectors) {
|
|
164
|
+
try {
|
|
165
|
+
const el = doc.querySelector(sel);
|
|
166
|
+
if (el)
|
|
167
|
+
return el;
|
|
168
|
+
} catch {}
|
|
169
|
+
}
|
|
170
|
+
return null;
|
|
171
|
+
};
|
|
172
|
+
var querySelectorAll = (doc, selectors) => {
|
|
173
|
+
const results = [];
|
|
174
|
+
for (const sel of selectors) {
|
|
175
|
+
try {
|
|
176
|
+
results.push(...Array.from(doc.querySelectorAll(sel)));
|
|
177
|
+
} catch {}
|
|
178
|
+
}
|
|
179
|
+
return results;
|
|
180
|
+
};
|
|
181
|
+
var getTextContent = (el) => el?.textContent?.trim() || null;
|
|
182
|
+
var buildAmazonProbe = (url, doc, config = BUNDLED_AMAZON_SELECTOR_CONFIG) => {
|
|
183
|
+
const { selectors } = config;
|
|
184
|
+
const pageType = detectPageType(url);
|
|
185
|
+
const hasLoginIndicator = selectors.loginIndicators.some((s) => {
|
|
186
|
+
try {
|
|
187
|
+
return !!doc.querySelector(s);
|
|
188
|
+
} catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
});
|
|
192
|
+
const hasLoggedOutIndicator = selectors.loggedOutIndicators.some((s) => {
|
|
193
|
+
try {
|
|
194
|
+
return !!doc.querySelector(s);
|
|
195
|
+
} catch {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
const isLoggedIn = hasLoginIndicator ? true : hasLoggedOutIndicator ? false : null;
|
|
200
|
+
let product = null;
|
|
201
|
+
if (pageType === "product") {
|
|
202
|
+
product = {
|
|
203
|
+
title: getTextContent(querySelector(doc, selectors.productTitle)),
|
|
204
|
+
price: getTextContent(querySelector(doc, selectors.productPrice)),
|
|
205
|
+
hasAddToCart: !!querySelector(doc, selectors.addToCartButton),
|
|
206
|
+
hasBuyNow: !!querySelector(doc, selectors.buyNowButton),
|
|
207
|
+
hasOptions: selectors.optionSelectors.some((s) => {
|
|
208
|
+
try {
|
|
209
|
+
return !!doc.querySelector(s);
|
|
210
|
+
} catch {
|
|
211
|
+
return false;
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
let cart = null;
|
|
217
|
+
if (pageType === "cart") {
|
|
218
|
+
const items = querySelectorAll(doc, selectors.cartItems);
|
|
219
|
+
cart = {
|
|
220
|
+
itemCount: items.length,
|
|
221
|
+
total: getTextContent(querySelector(doc, selectors.cartTotal)),
|
|
222
|
+
hasProceedToCheckout: !!querySelector(doc, selectors.proceedToCheckout)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
let search = null;
|
|
226
|
+
if (pageType === "search") {
|
|
227
|
+
const searchInput = querySelector(doc, selectors.searchInput);
|
|
228
|
+
const results = querySelectorAll(doc, selectors.searchResults);
|
|
229
|
+
search = {
|
|
230
|
+
query: searchInput?.value || new URL(url).searchParams.get("k") || null,
|
|
231
|
+
resultCount: results.length
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
return { url, pageType, isLoggedIn, product, cart, search };
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
// extension/content/amazon.ts
|
|
238
|
+
var createRequestId = () => globalThis.crypto?.randomUUID?.() ?? `req_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
239
|
+
var sendBackgroundEnvelope = async (envelope) => {
|
|
240
|
+
try {
|
|
241
|
+
await chrome.runtime.sendMessage(envelope);
|
|
242
|
+
} catch {}
|
|
243
|
+
};
|
|
244
|
+
var handleProbe = (message) => {
|
|
245
|
+
try {
|
|
246
|
+
const probe = buildAmazonProbe(window.location.href, document, BUNDLED_AMAZON_SELECTOR_CONFIG);
|
|
247
|
+
return createEnvelope("amazon:probe-result", { probe }, {
|
|
248
|
+
source: "extension:content",
|
|
249
|
+
messageId: createRequestId(),
|
|
250
|
+
requestId: message.messageId
|
|
251
|
+
});
|
|
252
|
+
} catch (error) {
|
|
253
|
+
return createErrorEnvelope({
|
|
254
|
+
code: "internal_error",
|
|
255
|
+
message: error instanceof Error ? error.message : "Failed to inspect the Amazon page."
|
|
256
|
+
}, {
|
|
257
|
+
source: "extension:content",
|
|
258
|
+
messageId: createRequestId(),
|
|
259
|
+
requestId: message.messageId
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
chrome.runtime.onMessage.addListener((raw, _sender, sendResponse) => {
|
|
264
|
+
const decoded = decodeEnvelope(raw);
|
|
265
|
+
if (!decoded || !decoded.ok)
|
|
266
|
+
return;
|
|
267
|
+
const { envelope } = decoded;
|
|
268
|
+
switch (envelope.type) {
|
|
269
|
+
case "amazon:probe": {
|
|
270
|
+
const response = handleProbe(envelope);
|
|
271
|
+
sendBackgroundEnvelope(response);
|
|
272
|
+
sendResponse(response);
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
default:
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
});
|
|
279
|
+
sendBackgroundEnvelope(createEnvelope("content:ready", { platform: "amazon", url: window.location.href }, { source: "extension:content", messageId: createRequestId() }));
|
|
280
|
+
})();
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Buygent",
|
|
4
4
|
"version": "0.1.0",
|
|
5
|
-
"description": "Buygent BYOK bridge for
|
|
5
|
+
"description": "Buygent BYOK bridge for AI-assisted shopping on Coupang and Amazon.",
|
|
6
6
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAz9QWmSM8oHI1SIgpWOs2Qd+svxqlch1W0ctBFtbHK7oUw5y0RFfu5hmB1khciag0ii4jjvitdCMt2yj3Oug3Kc+NZ9wijP5oz+bwW8y+7u9hXjZu+eAZjHuclUvl+yBnL0Ny4altosfWA3x0Lih2S7ZCCrZR/Jx6Gk483XgZE9VBPz5Banu7hqF8qleWX0LvrJXwUp3RWFywWXdq1LswQLcQtx9niygpGMvGr1DKlJ8+iDw4Cn8XUCzp1+n74DBHAz6eKbl+5iOoFCLYxzOeEfs0ABXGlrL0ShF2mgwt/3+ucL2wTWe1atXOJHmmmhU3bK4anyFFUMzTsTA+Io96+wIDAQAB",
|
|
7
7
|
"permissions": [
|
|
8
8
|
"activeTab",
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
"tabs"
|
|
12
12
|
],
|
|
13
13
|
"host_permissions": [
|
|
14
|
-
"https://*.coupang.com/*"
|
|
14
|
+
"https://*.coupang.com/*",
|
|
15
|
+
"https://*.amazon.com/*",
|
|
16
|
+
"https://*.amazon.co.jp/*"
|
|
15
17
|
],
|
|
16
18
|
"background": {
|
|
17
19
|
"service_worker": "background.js",
|
|
@@ -26,6 +28,16 @@
|
|
|
26
28
|
"content/coupang.js"
|
|
27
29
|
],
|
|
28
30
|
"run_at": "document_idle"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"matches": [
|
|
34
|
+
"https://*.amazon.com/*",
|
|
35
|
+
"https://*.amazon.co.jp/*"
|
|
36
|
+
],
|
|
37
|
+
"js": [
|
|
38
|
+
"content/amazon.js"
|
|
39
|
+
],
|
|
40
|
+
"run_at": "document_idle"
|
|
29
41
|
}
|
|
30
42
|
],
|
|
31
43
|
"action": {
|
|
@@ -33,4 +45,4 @@
|
|
|
33
45
|
"default_popup": "popup.html"
|
|
34
46
|
},
|
|
35
47
|
"minimum_chrome_version": "120"
|
|
36
|
-
}
|
|
48
|
+
}
|
package/dist/native-host.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// @bun
|
|
3
|
-
import{createServer as q}from"net";import{existsSync as f,mkdirSync as w,readFileSync as b,rmSync as m}from"fs";import{homedir as R}from"os";import{dirname as g,join as h}from"path";import{fileURLToPath as V}from"url";var x="0.0.0-dev",E="0.
|
|
3
|
+
import{createServer as q}from"net";import{existsSync as f,mkdirSync as w,readFileSync as b,rmSync as m}from"fs";import{homedir as R}from"os";import{dirname as g,join as h}from"path";import{fileURLToPath as V}from"url";var x="0.0.0-dev",E="0.4.0",A=()=>{let e=g(V(import.meta.url));while(!0){let t=h(e,"package.json");if(f(t)){let o=JSON.parse(b(t,"utf8"));if(typeof o.version==="string"&&o.version.trim().length>0)return o.version}let r=g(e);if(r===e)break;e=r}return E==="0.4.0"?x:E},B=A(),T=1,c=h(R(),".buygent","chrome-extension","com.buygent.host.sock"),n=Buffer.alloc(0),s=new Map,p=new Map,l=new WeakMap,i=()=>globalThis.crypto?.randomUUID?.()??`req_${Date.now()}_${Math.random().toString(16).slice(2)}`,_=(e,t,r)=>({protocolVersion:T,type:e,messageId:r.messageId,...r.requestId?{requestId:r.requestId}:{},sentAt:r.sentAt??new Date().toISOString(),source:r.source,payload:t}),d=(e,t)=>_("error",e,t),O=(e)=>{if(!e||typeof e!=="object"||Array.isArray(e))return{ok:!1,error:d({code:"invalid_envelope",message:"Expected a JSON object envelope."},{source:"native-host",messageId:i()})};let t=e;if(t.protocolVersion!==T)return{ok:!1,error:d({code:"unsupported_protocol_version",message:`Unsupported protocolVersion: ${String(t.protocolVersion??"missing")}`},{source:"native-host",messageId:i()})};if(typeof t.type!=="string"||typeof t.messageId!=="string"||typeof t.sentAt!=="string"||typeof t.source!=="string")return{ok:!1,error:d({code:"invalid_envelope",message:"Envelope must include type, messageId, sentAt, and source fields."},{source:"native-host",messageId:i()})};return{ok:!0,envelope:e}},D=(e)=>JSON.stringify(e),u=(e)=>{let t=Buffer.from(JSON.stringify(e),"utf8"),r=Buffer.alloc(4);r.writeUInt32LE(t.length,0),process.stdout.write(r),process.stdout.write(t)},a=(e,t)=>{e.write(`${D(t)}
|
|
4
4
|
`)},P=(e,t)=>{let r=p.get(e)??new Set;r.add(t),p.set(e,r)},S=(e)=>{for(let[t,r]of p.entries())if(r.delete(e),r.size===0)p.delete(t);for(let[t,r]of s.entries())if(r===e)s.delete(t)},k=(e,t)=>{let r=p.get(e);if(!r)return;for(let o of r)a(o,t)},U=(e)=>{if(e.type==="native:ping"){u(_("native:pong",{hostVersion:B,receivedAt:new Date().toISOString()},{source:"native-host",messageId:i(),requestId:e.messageId}));return}if(e.type==="order:ack"){if(e.requestId){let t=s.get(e.requestId);if(t)P(e.payload.orderId,t),a(t,e)}return}if(e.type==="runtime:state"){if(e.requestId){let t=s.get(e.requestId);if(t)s.delete(e.requestId),a(t,e)}return}if(e.type==="order:status"||e.type==="order:checkpoint"){k(e.payload.orderId,e);return}if(e.type==="error"){let t=typeof e.payload.details==="object"&&e.payload.details&&!Array.isArray(e.payload.details)?e.payload.details.orderId:void 0;if(typeof t==="string"){k(t,e);return}if(e.requestId){let r=s.get(e.requestId);if(r)s.delete(e.requestId),a(r,e)}}},L=(e)=>{let t=O(e);if(!t.ok){u(t.error);return}U(t.envelope)},H=(e,t)=>{let r=O(JSON.parse(t));if(!r.ok){a(e,r.error);return}let o=r.envelope;if(o.type==="order:start"||o.type==="runtime:get-state"){s.set(o.messageId,e),u(o);return}a(e,d({code:"unsupported_message",message:`Unsupported CLI/native-host message: ${o.type}`},{source:"native-host",messageId:i(),requestId:o.messageId}))},J=()=>{if(w(g(c),{recursive:!0,mode:448}),f(c))m(c,{force:!0})};J();var M=q((e)=>{e.setEncoding("utf8"),l.set(e,""),e.on("data",(t)=>{let o=`${l.get(e)??""}${t}`.split(/\r?\n/u);l.set(e,o.pop()??"");for(let N of o){let y=N.trim();if(!y)continue;try{H(e,y)}catch(I){a(e,d({code:"invalid_message",message:I instanceof Error?I.message:"Invalid CLI/native-host payload."},{source:"native-host",messageId:i()}))}}}),e.on("close",()=>{S(e)}),e.on("error",()=>{S(e)})});M.listen(c);process.stdin.on("data",(e)=>{n=Buffer.concat([n,e]);while(n.length>=4){let t=n.readUInt32LE(0);if(n.length<4+t)return;let r=n.subarray(4,4+t);n=n.subarray(4+t);try{L(JSON.parse(r.toString("utf8")))}catch(o){u(d({code:"invalid_message",message:o instanceof Error?o.message:"Invalid JSON payload."},{source:"native-host",messageId:i()}))}}});var v=()=>{if(M.close(),f(c))m(c,{force:!0})};process.stdin.on("end",()=>{v(),process.exit(0)});process.on("SIGTERM",()=>{v(),process.exit(0)});process.on("SIGINT",()=>{v(),process.exit(0)});process.stdin.resume();
|