nexscope-cli 0.1.0__py3-none-any.whl
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.
- nexscope_cli/__init__.py +4 -0
- nexscope_cli/api-contract.json +37 -0
- nexscope_cli/capabilities/__init__.py +3 -0
- nexscope_cli/capabilities/catalog.py +73 -0
- nexscope_cli/capabilities/mock.py +53 -0
- nexscope_cli/capabilities/plan.py +142 -0
- nexscope_cli/catalog.json +69 -0
- nexscope_cli/compat/__init__.py +4 -0
- nexscope_cli/compat/legacy.py +143 -0
- nexscope_cli/core/__init__.py +3 -0
- nexscope_cli/core/auth.py +81 -0
- nexscope_cli/core/datasets.py +305 -0
- nexscope_cli/core/errors.py +11 -0
- nexscope_cli/core/evidence.py +134 -0
- nexscope_cli/core/execution.py +171 -0
- nexscope_cli/core/remote.py +22 -0
- nexscope_cli/core/transport.py +61 -0
- nexscope_cli/dependencies.lock +282 -0
- nexscope_cli/dependency-lock.json +371 -0
- nexscope_cli/doctor.py +136 -0
- nexscope_cli/main.py +313 -0
- nexscope_cli/reports/__init__.py +3 -0
- nexscope_cli/reports/compare.py +135 -0
- nexscope_cli/reports/formats.py +99 -0
- nexscope_cli/resources/__init__.py +3 -0
- nexscope_cli/resources/fields.json +33 -0
- nexscope_cli/resources/mock-products.json +9 -0
- nexscope_cli/resources/products-sample.csv +5 -0
- nexscope_cli/resources/products-sample.json +6 -0
- nexscope_cli/resources/products-template.csv +1 -0
- nexscope_cli/resources/products.schema.json +182 -0
- nexscope_cli/resources/report.html +15 -0
- nexscope_cli/runner.py +43 -0
- nexscope_cli/skills/amazon-product-detail/SKILL.md +170 -0
- nexscope_cli/skills/amazon-product-detail/references/api.md +239 -0
- nexscope_cli/skills/amazon-product-detail/requirements.txt +1 -0
- nexscope_cli/skills/amazon-product-detail/scripts/amazon_product_detail.py +26 -0
- nexscope_cli/skills/amazon-search/SKILL.md +197 -0
- nexscope_cli/skills/amazon-search/references/api.md +189 -0
- nexscope_cli/skills/amazon-search/requirements.txt +1 -0
- nexscope_cli/skills/amazon-search/scripts/amazon_search.py +26 -0
- nexscope_cli/workflows/__init__.py +3 -0
- nexscope_cli/workflows/compare.py +213 -0
- nexscope_cli-0.1.0.dist-info/METADATA +257 -0
- nexscope_cli-0.1.0.dist-info/RECORD +74 -0
- nexscope_cli-0.1.0.dist-info/WHEEL +5 -0
- nexscope_cli-0.1.0.dist-info/entry_points.txt +2 -0
- nexscope_cli-0.1.0.dist-info/licenses/ASSET-NOTICES.md +9 -0
- nexscope_cli-0.1.0.dist-info/licenses/LICENSE +202 -0
- nexscope_cli-0.1.0.dist-info/licenses/NOTICE +20 -0
- nexscope_cli-0.1.0.dist-info/licenses/THIRD-PARTY-NOTICES.md +27 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/backports-tarfile/LICENSE +17 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/certifi/LICENSE +20 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cffi/LICENSE +23 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/charset-normalizer/LICENSE +21 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cryptography/LICENSE +3 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cryptography/LICENSE.APACHE +202 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cryptography/LICENSE.BSD +27 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/idna/LICENSE.md +31 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/importlib-metadata/LICENSE +73 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jaraco-classes/LICENSE +17 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jaraco-context/LICENSE +18 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jaraco-functools/LICENSE +18 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jeepney/LICENSE +21 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/keyring/LICENSE +18 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/more-itertools/LICENSE +19 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/pycparser/LICENSE +27 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/pywin32-ctypes/LICENSE.txt +28 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/requests/LICENSE +175 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/requests/NOTICE +2 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/secretstorage/LICENSE +25 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/urllib3/LICENSE.txt +21 -0
- nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/zipp/LICENSE +18 -0
- nexscope_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
# Copyright 2026 Nexscope
|
|
2
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
"""A single task entry: plan, optional search, details, evidence and report."""
|
|
4
|
+
from datetime import datetime, timezone
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
|
|
8
|
+
from ..capabilities.catalog import DOMAINS
|
|
9
|
+
from ..capabilities.plan import plan
|
|
10
|
+
from ..core.datasets import product
|
|
11
|
+
from ..core.evidence import Artifacts, decode, read_bundle, validate_request
|
|
12
|
+
from ..core.errors import Failure
|
|
13
|
+
from ..reports.compare import render_format
|
|
14
|
+
from ..reports.formats import FORMATS
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def read_candidates(folder):
|
|
18
|
+
bundle = read_bundle(folder)
|
|
19
|
+
if set(bundle) != {'plan.json', 'request-1.json', 'candidates.json', 'result.json'}:
|
|
20
|
+
raise Failure('Resume requires a completed candidates-only output; failed calls are never replayed.')
|
|
21
|
+
previous = decode(bundle['plan.json'])
|
|
22
|
+
result = decode(bundle['result.json'])
|
|
23
|
+
search = validate_request(decode(bundle['request-1.json']))
|
|
24
|
+
if (not isinstance(previous, dict) or previous.get('schemaVersion') != 'nexscope.comparison-plan.v1'
|
|
25
|
+
or previous.get('status') != 'PLANNED' or type(previous.get('maxCalls')) is not int
|
|
26
|
+
or previous.get('marketplace') not in DOMAINS
|
|
27
|
+
or previous.get('authMode') not in ('browser-account', 'env-api-key')
|
|
28
|
+
or previous.get('asins') is not None or previous.get('selectedAsins') is not None
|
|
29
|
+
or previous.get('importedSearch') is not None or not previous.get('keyword')
|
|
30
|
+
or not isinstance(result, dict) or result.get('schemaVersion') != 'nexscope.comparison-result.v1'
|
|
31
|
+
or result.get('status') != 'CANDIDATES_READY' or result.get('error') != 'SELECTION_REQUIRED'
|
|
32
|
+
or result.get('sourceType') not in ('mock', 'remote')):
|
|
33
|
+
raise Failure('Resume requires a versioned CANDIDATES_READY comparison checkpoint.')
|
|
34
|
+
rebuilt = workflow_plan(keyword=previous['keyword'], market=previous.get('marketplace'),
|
|
35
|
+
max_calls=previous.get('maxCalls'), auth_mode=previous.get('authMode'))
|
|
36
|
+
execution = previous.get('execution')
|
|
37
|
+
if (not isinstance(execution, dict) or execution.get('sourceType') != result['sourceType']
|
|
38
|
+
or execution.get('origin') != search['origin'] or execution.get('requestsSent') != 0
|
|
39
|
+
or previous.get('maximumRequests') != 1 or search['capability'] != 'amazon-search'
|
|
40
|
+
or search['status'] != 'SUCCESS' or search['cacheHit'] or search['sequence'] != 1
|
|
41
|
+
or search['authMode'] != rebuilt['authMode'] or search['params'] != rebuilt['steps'][0]['requests'][0]['body']
|
|
42
|
+
or not isinstance(previous.get('steps'), list) or len(previous['steps']) != 1
|
|
43
|
+
or not isinstance(previous['steps'][0], dict)
|
|
44
|
+
or previous['steps'][0].get('requests') != rebuilt['steps'][0]['requests']
|
|
45
|
+
or previous['steps'][0].get('capability') != 'amazon-search'
|
|
46
|
+
or previous['steps'][0].get('contractVersion') != search['contractVersion']
|
|
47
|
+
or previous['steps'][0].get('source') != search['source']
|
|
48
|
+
or not isinstance(previous['steps'][0].get('auth'), dict)
|
|
49
|
+
or previous['steps'][0]['auth'].get('mode') != search['authMode']
|
|
50
|
+
or previous['steps'][0]['auth'].get('origin') != search['origin']
|
|
51
|
+
or (result['sourceType'] == 'mock' and search['origin'] != 'https://mock.invalid')):
|
|
52
|
+
raise Failure('Checkpoint plan and search evidence disagree.')
|
|
53
|
+
candidates = search['response']['data']['products']
|
|
54
|
+
budget = result.get('budget')
|
|
55
|
+
if (decode(bundle['candidates.json']) != candidates or result.get('candidates') != candidates
|
|
56
|
+
or not isinstance(budget, dict) or type(budget.get('requestsSent')) is not int or budget['requestsSent'] != 1
|
|
57
|
+
or budget.get('maxCalls') != rebuilt['maxCalls'] or budget.get('cacheHits') != 0
|
|
58
|
+
or budget.get('stoppedAfterFailure') is not False):
|
|
59
|
+
raise Failure('Checkpoint candidates or accounting disagree with search evidence.')
|
|
60
|
+
return previous, result, search, bundle
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def workflow_plan(*, asins=None, keyword=None, resume=None, selected=None, market=None, max_calls=None, auth_mode=None, api_base=None):
|
|
64
|
+
if sum(bool(value) for value in (asins, keyword, resume)) != 1:
|
|
65
|
+
raise Failure('Provide exactly one of asins, keyword or resume.')
|
|
66
|
+
history = None
|
|
67
|
+
if resume:
|
|
68
|
+
previous, result, search, bundle = read_candidates(resume)
|
|
69
|
+
if ((market is not None and market != previous['marketplace'])
|
|
70
|
+
or (auth_mode is not None and auth_mode != previous['authMode'])
|
|
71
|
+
or (api_base is not None and api_base.rstrip('/') != search['origin'])):
|
|
72
|
+
raise Failure('Resume must keep the original marketplace, authentication mode and origin.')
|
|
73
|
+
market, auth_mode = previous['marketplace'], previous['authMode']
|
|
74
|
+
if auth_mode == 'browser-account':
|
|
75
|
+
api_base = search['origin']
|
|
76
|
+
max_calls = previous['maxCalls'] if max_calls is None else max_calls
|
|
77
|
+
if type(max_calls) is not int or max_calls > previous['maxCalls']:
|
|
78
|
+
raise Failure('Resume cannot increase the original max-calls budget.')
|
|
79
|
+
if not selected:
|
|
80
|
+
raise Failure('Resume requires select-asins from the saved candidates.')
|
|
81
|
+
if not set(selected.split(',')) <= {row['asin'] for row in search['response']['data']['products']}:
|
|
82
|
+
raise Failure('Selected ASINs must come from the saved search evidence.')
|
|
83
|
+
history = {'historical': True, 'sourceType': result['sourceType'], 'authMode': search['authMode'],
|
|
84
|
+
'origin': search['origin'], 'fetchedAt': search['fetchedAt'], 'cacheHit': False,
|
|
85
|
+
'evidenceFile': 'request-1.json', 'planFile': 'search-plan.json',
|
|
86
|
+
'planSha256': hashlib.sha256(bundle['plan.json']).hexdigest(), 'requestsSent': 1}
|
|
87
|
+
market = market if market is not None else 'amazon.com'
|
|
88
|
+
auth_mode = auth_mode if auth_mode is not None else 'browser-account'
|
|
89
|
+
max_calls = max_calls if max_calls is not None else 4
|
|
90
|
+
if market not in DOMAINS:
|
|
91
|
+
raise Failure('Choose a supported Amazon marketplace.')
|
|
92
|
+
if selected and not (keyword or resume):
|
|
93
|
+
raise Failure('select-asins is only valid with keyword search.')
|
|
94
|
+
if asins or selected:
|
|
95
|
+
detail = plan('amazon-product-detail', {'asins': asins or selected, 'amazonDomain': market}, auth_mode=auth_mode, max_calls=max_calls, api_base=api_base)
|
|
96
|
+
if len(detail['requests']) < 2:
|
|
97
|
+
raise Failure('A comparison requires two or three distinct ASINs.')
|
|
98
|
+
else:
|
|
99
|
+
detail = None
|
|
100
|
+
search = plan('amazon-search', {'keyword': keyword, 'amazonDomain': market}, auth_mode=auth_mode, max_calls=max_calls, api_base=api_base) if keyword else None
|
|
101
|
+
maximum = (len(detail['requests']) if detail else 0) + (1 if search or history else 0)
|
|
102
|
+
if maximum > max_calls:
|
|
103
|
+
raise Failure('Search plus details exceeds max-calls; no requests were sent.')
|
|
104
|
+
return {'schemaVersion': 'nexscope.comparison-plan.v1', 'status': 'PLANNED',
|
|
105
|
+
'authMode': auth_mode, 'marketplace': market, 'asins': asins, 'keyword': keyword,
|
|
106
|
+
'selectedAsins': selected, 'maximumRequests': maximum, 'maxCalls': max_calls,
|
|
107
|
+
'estimatedCredits': None, 'billingCap': None, 'requestsSent': 1 if history else 0,
|
|
108
|
+
'newRequests': maximum - (1 if history else 0), 'importedSearch': history,
|
|
109
|
+
'selectionPolicy': 'explicit-ASINs-only; otherwise return candidates',
|
|
110
|
+
'steps': [item for item in (search, detail) if item], 'liveExecutionAvailable': False}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def compare(executor, folder, *, asins=None, keyword=None, resume=None, selected=None, market=None, mock=False, format='html', allow_paid=False):
|
|
114
|
+
if not mock and not allow_paid:
|
|
115
|
+
raise Failure('LIVE_EXECUTION_UNAVAILABLE: remote execution requires explicit --allow-paid authorization.')
|
|
116
|
+
source_type = 'mock' if mock else 'remote'
|
|
117
|
+
if format not in (*FORMATS, 'all'):
|
|
118
|
+
raise Failure('Choose html, csv, markdown or all.')
|
|
119
|
+
execution_plan = workflow_plan(asins=asins, keyword=keyword, resume=resume, selected=selected, market=market,
|
|
120
|
+
max_calls=executor.max_calls, auth_mode=executor.context.mode,
|
|
121
|
+
api_base=executor.context.base if executor.context.mode == 'browser-account' else None)
|
|
122
|
+
market = execution_plan['marketplace']
|
|
123
|
+
if executor.sent or executor.results or executor.imported_results:
|
|
124
|
+
raise Failure('A comparison requires a fresh executor and its own request budget.')
|
|
125
|
+
imported = execution_plan['importedSearch']
|
|
126
|
+
if imported and (imported['sourceType'] != source_type or imported['origin'] != executor.context.base):
|
|
127
|
+
raise Failure('Resume must keep the original source type and authentication origin.')
|
|
128
|
+
execution_plan['execution'] = {'sourceType': source_type, 'origin': executor.context.base, 'requestsSent': 0}
|
|
129
|
+
execution_plan['liveExecutionAvailable'] = allow_paid and not mock
|
|
130
|
+
execution_plan['reportFormat'] = format
|
|
131
|
+
for step in execution_plan['steps']:
|
|
132
|
+
step['auth'].update(origin=executor.context.base, originSource='fictional mock transport' if mock else 'selected authentication context')
|
|
133
|
+
artifacts = Artifacts(folder, execution_plan)
|
|
134
|
+
if resume:
|
|
135
|
+
_, _, historical_search, bundle = read_candidates(resume)
|
|
136
|
+
artifacts.write_bytes('search-plan.json', bundle['plan.json'])
|
|
137
|
+
artifacts.write_bytes('search-result.json', bundle['result.json'])
|
|
138
|
+
for name in ('request-1.json', 'candidates.json'):
|
|
139
|
+
artifacts.write_bytes(name, bundle[name])
|
|
140
|
+
executor.imported_results = [historical_search]
|
|
141
|
+
executor.record = artifacts.record
|
|
142
|
+
chosen = asins or selected
|
|
143
|
+
if keyword:
|
|
144
|
+
search = executor.execute('amazon-search', {'keyword': keyword, 'amazonDomain': market})[0]
|
|
145
|
+
candidates = search['response']['data']['products'] if search['status'] == 'SUCCESS' else []
|
|
146
|
+
artifacts.write('candidates.json', candidates)
|
|
147
|
+
available = {row['asin'] for row in candidates}
|
|
148
|
+
if search['status'] != 'SUCCESS' or not chosen or not set(chosen.split(',')) <= available:
|
|
149
|
+
status = 'CANDIDATES_READY' if search['status'] == 'SUCCESS' and not chosen else 'FAILED'
|
|
150
|
+
result = {'schemaVersion': 'nexscope.comparison-result.v1', 'status': status, 'sourceType': source_type, 'report': None,
|
|
151
|
+
'error': 'SELECTION_REQUIRED' if status == 'CANDIDATES_READY' else 'SEARCH_OR_SELECTION_FAILED',
|
|
152
|
+
'candidates': candidates, 'output': str(artifacts.root), 'budget': executor.summary()}
|
|
153
|
+
artifacts.write('result.json', dict(result, output='.'))
|
|
154
|
+
artifacts.finish()
|
|
155
|
+
return result
|
|
156
|
+
details = executor.execute('amazon-product-detail', {'asins': chosen, 'amazonDomain': market})
|
|
157
|
+
rows = [product(result, market) for result in details if result['status'] == 'SUCCESS']
|
|
158
|
+
for row in rows:
|
|
159
|
+
evidence_file = artifacts.root / row['extra']['evidenceFile']
|
|
160
|
+
row['extra']['evidenceSha256'] = hashlib.sha256(evidence_file.read_bytes()).hexdigest()
|
|
161
|
+
errors = [{'asin': result['params']['asins'], 'status': result['status'], 'error': result['error']}
|
|
162
|
+
for result in details if result['status'] != 'SUCCESS']
|
|
163
|
+
status = ('COMPLETE' if not errors else 'PARTIAL') if len(rows) >= 2 else 'INSUFFICIENT_DATA'
|
|
164
|
+
digest = hashlib.sha256((artifacts.root / 'plan.json').read_bytes()).hexdigest()
|
|
165
|
+
dataset = {'schemaVersion': 1, 'sourceType': source_type, 'inputName': 'plan.json', 'inputSha256': digest,
|
|
166
|
+
'importedAt': datetime.now(timezone.utc).isoformat(), 'products': rows,
|
|
167
|
+
'collectionStatus': status, 'errors': errors, 'budget': executor.summary()}
|
|
168
|
+
artifacts.write('dataset.json', dataset)
|
|
169
|
+
result = {'schemaVersion': 'nexscope.comparison-result.v1', 'status': status, 'sourceType': source_type, 'requested': len(chosen.split(',')),
|
|
170
|
+
'succeeded': len(rows), 'errors': errors, 'budget': executor.summary(),
|
|
171
|
+
'output': str(artifacts.root), 'report': None, 'reports': {},
|
|
172
|
+
'liveRequests': 0 if mock else executor.summary()['requestsSent'],
|
|
173
|
+
'liveRequestsThisRun': 0 if mock else executor.sent}
|
|
174
|
+
if len(rows) >= 2:
|
|
175
|
+
# Report beside its evidence; all links stay relative when sharing folder.
|
|
176
|
+
for selected_format in FORMATS if format == 'all' else (format,):
|
|
177
|
+
report = artifacts.root / ('comparison' + FORMATS[selected_format])
|
|
178
|
+
with report.open('x', encoding='utf-8', newline='') as stream:
|
|
179
|
+
stream.write(render_format(dataset, '.', selected_format))
|
|
180
|
+
result['reports'][selected_format] = str(report)
|
|
181
|
+
result['report'] = next(iter(result['reports'].values()))
|
|
182
|
+
result['reportFormat'] = format
|
|
183
|
+
artifacts.write('result.json', dict(result, output='.',
|
|
184
|
+
report=next((('comparison' + FORMATS[name]) for name in result['reports']), None),
|
|
185
|
+
reports={name: 'comparison' + FORMATS[name] for name in result['reports']}))
|
|
186
|
+
artifacts.finish()
|
|
187
|
+
return result
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def command(args):
|
|
191
|
+
if args.mock and args.api_base is not None:
|
|
192
|
+
raise Failure('Mock workflow uses a fictional origin only; omit --api-base.')
|
|
193
|
+
execution_plan = workflow_plan(asins=args.asins, keyword=args.keyword, resume=args.resume, selected=args.select_asins,
|
|
194
|
+
market=args.market, max_calls=args.max_calls, auth_mode=args.auth_mode, api_base=args.api_base)
|
|
195
|
+
args.market, args.max_calls, args.auth_mode = execution_plan['marketplace'], execution_plan['maxCalls'], execution_plan['authMode']
|
|
196
|
+
imported = execution_plan['importedSearch']
|
|
197
|
+
if imported and imported['sourceType'] != ('mock' if args.mock else 'remote'):
|
|
198
|
+
raise Failure('Resume must keep the original source type; choose the same mock or remote execution mode.')
|
|
199
|
+
if args.dry_run:
|
|
200
|
+
execution_plan['reportFormat'] = args.format
|
|
201
|
+
return execution_plan
|
|
202
|
+
if not args.mock and not args.allow_paid:
|
|
203
|
+
raise Failure('LIVE_EXECUTION_UNAVAILABLE: --execute requires --mock or explicit --allow-paid.')
|
|
204
|
+
if args.mock and args.allow_paid:
|
|
205
|
+
raise Failure('Choose mock execution or paid execution, not both.')
|
|
206
|
+
if not args.out:
|
|
207
|
+
raise Failure('Choose a new output directory with --out.')
|
|
208
|
+
if imported and args.auth_mode == 'browser-account':
|
|
209
|
+
args.api_base = imported['origin']
|
|
210
|
+
from ..core.remote import engine_for
|
|
211
|
+
engine = engine_for(args)
|
|
212
|
+
return compare(engine, args.out, asins=args.asins, keyword=args.keyword, resume=args.resume,
|
|
213
|
+
selected=args.select_asins, market=args.market, mock=bool(args.mock), format=args.format, allow_paid=args.allow_paid)
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: nexscope-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Offline product comparison reports and bounded Nexscope ecommerce data workflows
|
|
5
|
+
Author: Nexscope
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Repository, https://github.com/nexscope-ai/nexscope-cli
|
|
8
|
+
Project-URL: Issues, https://github.com/nexscope-ai/nexscope-cli/issues
|
|
9
|
+
Requires-Python: >=3.11
|
|
10
|
+
Description-Content-Type: text/markdown
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
License-File: NOTICE
|
|
13
|
+
License-File: ASSET-NOTICES.md
|
|
14
|
+
License-File: THIRD-PARTY-NOTICES.md
|
|
15
|
+
License-File: third-party-notices/backports-tarfile/LICENSE
|
|
16
|
+
License-File: third-party-notices/certifi/LICENSE
|
|
17
|
+
License-File: third-party-notices/cffi/LICENSE
|
|
18
|
+
License-File: third-party-notices/charset-normalizer/LICENSE
|
|
19
|
+
License-File: third-party-notices/cryptography/LICENSE
|
|
20
|
+
License-File: third-party-notices/cryptography/LICENSE.APACHE
|
|
21
|
+
License-File: third-party-notices/cryptography/LICENSE.BSD
|
|
22
|
+
License-File: third-party-notices/idna/LICENSE.md
|
|
23
|
+
License-File: third-party-notices/importlib-metadata/LICENSE
|
|
24
|
+
License-File: third-party-notices/jaraco-classes/LICENSE
|
|
25
|
+
License-File: third-party-notices/jaraco-context/LICENSE
|
|
26
|
+
License-File: third-party-notices/jaraco-functools/LICENSE
|
|
27
|
+
License-File: third-party-notices/jeepney/LICENSE
|
|
28
|
+
License-File: third-party-notices/keyring/LICENSE
|
|
29
|
+
License-File: third-party-notices/more-itertools/LICENSE
|
|
30
|
+
License-File: third-party-notices/pycparser/LICENSE
|
|
31
|
+
License-File: third-party-notices/pywin32-ctypes/LICENSE.txt
|
|
32
|
+
License-File: third-party-notices/requests/LICENSE
|
|
33
|
+
License-File: third-party-notices/requests/NOTICE
|
|
34
|
+
License-File: third-party-notices/secretstorage/LICENSE
|
|
35
|
+
License-File: third-party-notices/urllib3/LICENSE.txt
|
|
36
|
+
License-File: third-party-notices/zipp/LICENSE
|
|
37
|
+
Requires-Dist: keyring<26,>=25
|
|
38
|
+
Requires-Dist: requests<3,>=2.32
|
|
39
|
+
Dynamic: license-file
|
|
40
|
+
|
|
41
|
+
# Nexscope CLI
|
|
42
|
+
|
|
43
|
+
Create product comparison reports from your own CSV or JSON, entirely offline.
|
|
44
|
+
No account, API key, keyring, LLM or upload is needed. HTML, CSV and Markdown
|
|
45
|
+
reports include the exact input, validated rows and evidence hashes.
|
|
46
|
+
|
|
47
|
+
This is the independent CLI repository. Its first version is **0.1.0**.
|
|
48
|
+
Verified behavior and remaining acceptance limits are recorded in ACCEPTANCE.md.
|
|
49
|
+
The published Codex plugin and independent Skills are separate distributions.
|
|
50
|
+
|
|
51
|
+
## Install and try your own data
|
|
52
|
+
|
|
53
|
+
Python 3.11 or newer is required. Build the wheel from this repository, or obtain
|
|
54
|
+
the exact reviewed wheel and its SHA-256 from a release of this repository.
|
|
55
|
+
Do not install an unverified package just because it has the same name.
|
|
56
|
+
|
|
57
|
+
From source on macOS or Linux:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
git clone https://github.com/nexscope-ai/nexscope-cli.git
|
|
61
|
+
cd nexscope-cli
|
|
62
|
+
python3 -m venv .venv
|
|
63
|
+
. .venv/bin/activate
|
|
64
|
+
python -m pip wheel --no-deps --wheel-dir dist .
|
|
65
|
+
python -m pip install --no-index --no-deps dist/nexscope_cli-0.1.0-py3-none-any.whl
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
On Windows PowerShell, create the environment with `py -3 -m venv .venv` and use
|
|
69
|
+
`.venv\Scripts\python.exe` for the Python commands; activation is optional.
|
|
70
|
+
There is no need to change PowerShell's execution policy.
|
|
71
|
+
|
|
72
|
+
If you already have the reviewed wheel, install it with:
|
|
73
|
+
|
|
74
|
+
```sh
|
|
75
|
+
python -m pip install --no-index --no-deps /absolute/path/nexscope_cli-0.1.0-py3-none-any.whl
|
|
76
|
+
nexscope doctor --offline
|
|
77
|
+
nexscope report template --format csv --out products.csv
|
|
78
|
+
nexscope report describe --schema
|
|
79
|
+
nexscope report demo --format json --out sample.json
|
|
80
|
+
nexscope report compare --input sample.json --format html --out comparison.html
|
|
81
|
+
nexscope report compare --input sample.json --format csv --out comparison.csv
|
|
82
|
+
nexscope report compare --input sample.json --format markdown --out comparison.md
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Use fresh output names. Reports never overwrite files or silently fall back to
|
|
86
|
+
another directory. Keep each report with its sibling `.evidence` directory.
|
|
87
|
+
The samples are fictional. `nexscope demo product-comparison --out demo-output`
|
|
88
|
+
also exercises the complete three-product workflow without credentials or network.
|
|
89
|
+
|
|
90
|
+
`product_id` and `title` are required. A price requires an explicit currency;
|
|
91
|
+
a rating requires its explicit positive scale. Missing values stay unknown.
|
|
92
|
+
Identifiers must be strings and unique within a marketplace. Additional input
|
|
93
|
+
columns are retained as metadata. There is no inferred sales, profit, currency
|
|
94
|
+
conversion or ranking across different markets, currencies or rating scales.
|
|
95
|
+
Remote currency symbols such as `$` are ambiguous. The CLI keeps the product and
|
|
96
|
+
records `unresolvedCurrency`/`unresolvedPrice` alongside its raw evidence, while
|
|
97
|
+
leaving comparable currency and price unknown. It never infers USD from a symbol
|
|
98
|
+
or marketplace. Known fictional search candidates retain their fields in mock
|
|
99
|
+
details. Report dry-runs validate parent directories and existing outputs before
|
|
100
|
+
export. Successful live keyword search and continuation remain unaccepted; see
|
|
101
|
+
ACCEPTANCE.md for the exact results.
|
|
102
|
+
|
|
103
|
+
Raw UTF-8 CSV/JSON inputs are limited to 10 MiB and 10,000 products. Local imports
|
|
104
|
+
have no three-product limit. CSV errors use logical record numbers including the
|
|
105
|
+
header; JSON errors use one-based array positions. Invalid rows are explained,
|
|
106
|
+
never silently dropped. Versioned datasets allow 32 MiB because normalization
|
|
107
|
+
adds metadata; evidence bundles allow 64 MiB. See `report describe` for fields.
|
|
108
|
+
|
|
109
|
+
## Product comparison workflows
|
|
110
|
+
|
|
111
|
+
Remote planning is free of network and credential access:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
nexscope workflow compare --asins B000000001,B000000002,B000000003 --dry-run
|
|
115
|
+
nexscope workflow compare --keyword lamp --dry-run
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The bounded workflow accepts two or three distinct ASINs, or one search page
|
|
119
|
+
followed by explicit selection of two or three candidates. Use `--mock complete`
|
|
120
|
+
for fictional execution. `--mock partial` demonstrates partial completion.
|
|
121
|
+
|
|
122
|
+
```sh
|
|
123
|
+
nexscope workflow compare --keyword lamp --execute --mock complete --max-calls 4 --out candidates
|
|
124
|
+
nexscope workflow compare --resume candidates --select-asins B000000001,B000000002 --dry-run --mock complete
|
|
125
|
+
nexscope workflow compare --resume candidates --select-asins B000000001,B000000002 --execute --mock complete --format all --out finished
|
|
126
|
+
nexscope report compare --input finished/dataset.json --format markdown --out another.md
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`--resume` imports the original search evidence, sends only the selected detail
|
|
130
|
+
requests and carries forward the original budget. One search plus two details
|
|
131
|
+
is three total requests. The original marketplace, source type, auth mode and
|
|
132
|
+
origin must agree; a resumed budget cannot increase. Omitted marketplace, auth
|
|
133
|
+
mode and max-calls inherit the checkpoint. `--keyword --select-asins` retains its
|
|
134
|
+
existing meaning of a new search plus details; use `--resume` after selection
|
|
135
|
+
to avoid paying for another search.
|
|
136
|
+
|
|
137
|
+
Only completed `CANDIDATES_READY` outputs with a valid manifest may be resumed.
|
|
138
|
+
Failed/incomplete outputs are never automatically replayed. A deliberate new
|
|
139
|
+
command using the same candidates starts another branch of work; there is no
|
|
140
|
+
server-side spending lock. `max-calls` is a request limit, not a monetary cap.
|
|
141
|
+
Ambiguous failures consume a call. No automatic retries, pagination or ASIN
|
|
142
|
+
expansion occur. Exit code 1 marks failures, partial or insufficient results;
|
|
143
|
+
inspect saved results and evidence before deciding what to do next.
|
|
144
|
+
|
|
145
|
+
Historical search imports are explicit user-selected evidence, not cache hits,
|
|
146
|
+
fresh live queries, proof of account identity or server-signed records. They are
|
|
147
|
+
never inserted into a credential context's cache. New requests use only the
|
|
148
|
+
currently selected credentials. Each finished directory contains the original
|
|
149
|
+
search plan/response, current plan, all details and hashes, with relative links.
|
|
150
|
+
|
|
151
|
+
`report compare` can read this version's `dataset.json` without refreshing data.
|
|
152
|
+
It checks the complete bundle, input digest, request contracts, product mappings,
|
|
153
|
+
selection and accounting, preserving `sourceType`, retrieval times, errors and
|
|
154
|
+
the original `inputName/inputSha256`. `report-input.json` separately identifies
|
|
155
|
+
the exact input selected for re-export. These hashes detect inconsistent edits
|
|
156
|
+
or corruption; they do not authenticate an author who can rewrite the whole
|
|
157
|
+
bundle. Do not treat third-party bundles as verified live market data.
|
|
158
|
+
Older preview datasets without manifests must be regenerated; this version does
|
|
159
|
+
not silently relabel them as user imports.
|
|
160
|
+
|
|
161
|
+
## Connect
|
|
162
|
+
|
|
163
|
+
Online browser/plugin use needs the existing hash-pinned dependency profile:
|
|
164
|
+
Set `APPROVED_API_ORIGIN` to the approved service origin. For paid data, set
|
|
165
|
+
`ASIN_1` and `ASIN_2` to verified real identifiers and explicitly approve the
|
|
166
|
+
request budget. The fictional identifiers used by mock examples are not live
|
|
167
|
+
test inputs; empty responses can still incur charges.
|
|
168
|
+
|
|
169
|
+
```sh
|
|
170
|
+
python -m pip install --require-hashes -r /absolute/checkout/cli/nexscope_cli/dependencies.lock
|
|
171
|
+
nexscope --api-base "$APPROVED_API_ORIGIN" auth login --source-client python-cli --source-channel github
|
|
172
|
+
# Complete the returned connection URL in your browser, then:
|
|
173
|
+
nexscope --api-base "$APPROVED_API_ORIGIN" auth poll
|
|
174
|
+
nexscope --api-base "$APPROVED_API_ORIGIN" account
|
|
175
|
+
nexscope --api-base "$APPROVED_API_ORIGIN" workflow compare --asins "$ASIN_1,$ASIN_2" --max-calls 2 --execute --allow-paid --out live-comparison
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Browser credentials use the OS credential store. Supported backends are Windows
|
|
179
|
+
Credential Manager, macOS Keychain, Secret Service and KWallet. The CLI does not
|
|
180
|
+
read an environment key in `browser-account` mode or fall back when login fails.
|
|
181
|
+
`--api-base` is a global option before the command when a different approved
|
|
182
|
+
browser API origin is needed. Authenticated redirects are refused.
|
|
183
|
+
|
|
184
|
+
Independent Skills and explicit `--auth-mode env-api-key` use only
|
|
185
|
+
`NEXSCOPE_API_KEY` and `NEXSCOPE_PROXY_BASE`, privately configured in your shell.
|
|
186
|
+
They require no browser login or keyring. Do not put keys into command history,
|
|
187
|
+
documents or Git. Omit `--api-base` in this mode. Credential failure never
|
|
188
|
+
switches modes. Cache entries are valid-success-only, memory-only, expire after
|
|
189
|
+
300 seconds and are isolated by immutable auth context, origin, capability,
|
|
190
|
+
parameters, marketplace and contract. Separate processes never share a cache.
|
|
191
|
+
|
|
192
|
+
`get-skills`, browser-account `run ... -- <old script arguments>` and `export`
|
|
193
|
+
remain available. The bundled standalone scripts require exactly this wheel
|
|
194
|
+
version and preserve their JSON argument, `--inline`, `--no-cache`, original
|
|
195
|
+
environment configuration and legacy response/exit semantics. They forward a
|
|
196
|
+
legacy batch/page once without the bounded workflow's three-ASIN/one-page limits.
|
|
197
|
+
Legacy JSON business errors can still exit zero: inspect the response code/error.
|
|
198
|
+
Do not replace published Skills with these adapters before installation and live
|
|
199
|
+
compatibility acceptance. No dependencies are auto-installed at query time.
|
|
200
|
+
|
|
201
|
+
Only Amazon search/detail are adapted. Credits are unknown (`null`); response
|
|
202
|
+
`cost` is not treated as a credit charge. Real account, balance and billing
|
|
203
|
+
acceptance remain separate gates. Reports contain no telemetry or automatic
|
|
204
|
+
upload. Review product data before sharing a report or its evidence.
|
|
205
|
+
|
|
206
|
+
## Development and verification
|
|
207
|
+
|
|
208
|
+
```sh
|
|
209
|
+
python -m pip install --no-deps .
|
|
210
|
+
python -m unittest discover -s tests -v
|
|
211
|
+
python scripts/check_resources.py
|
|
212
|
+
python -m pip wheel --no-deps --wheel-dir dist .
|
|
213
|
+
python scripts/verify_wheel.py dist/nexscope_cli-0.1.0-py3-none-any.whl
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
The standard-library test suite runs with no service credentials. The wheel
|
|
217
|
+
verification creates a fresh environment outside the checkout, clears source
|
|
218
|
+
path injection, installs the actual wheel without dependencies, blocks network
|
|
219
|
+
and keyring for offline checks, then runs the installed test suite and CLI cases.
|
|
220
|
+
It also tests old adapters against a fictional loopback server. Loopback fixtures
|
|
221
|
+
do not constitute live Nexscope acceptance. CI runs the same checks on Windows,
|
|
222
|
+
macOS and Linux with Python 3.11 and 3.12; a configured matrix is not a passing run.
|
|
223
|
+
|
|
224
|
+
Use a separate environment for upgrades and rollback. Install the prior exact
|
|
225
|
+
verified wheel, and restore any paired Skill directory to its matching version.
|
|
226
|
+
The verification script accepts `--previous-wheel /path/to/prior.whl` to exercise
|
|
227
|
+
upgrade/rollback while checking that user reports and test credential sentinels
|
|
228
|
+
survive. No public package, tag, release or plugin upload is made by these scripts.
|
|
229
|
+
|
|
230
|
+
On a machine with a real supported OS credential store, add `--with-os-keyring`
|
|
231
|
+
alongside `--with-dependencies` to test an isolated fictional credential. The
|
|
232
|
+
verifier checks write/read, upgrade/rollback and uninstall persistence, then
|
|
233
|
+
deletes only its own uniquely named entry. It does not log in to a service or
|
|
234
|
+
modify an existing account credential. A missing desktop credential store is
|
|
235
|
+
reported as a prerequisite failure; it never falls back to plaintext storage.
|
|
236
|
+
|
|
237
|
+
For an Ubuntu host without `ensurepip`, use `--pip-wheel /path/to/reviewed/pip.whl`
|
|
238
|
+
to bootstrap pip inside the fresh venv from an explicit trusted wheel, such as
|
|
239
|
+
Python's bundled ensurepip wheel. This does not install into the system Python.
|
|
240
|
+
Windows and Ubuntu WSL2 installation acceptance has been run locally; WSL2 does
|
|
241
|
+
not certify macOS or a Linux desktop credential store.
|
|
242
|
+
|
|
243
|
+
When intentionally changing a bundled adapter, run
|
|
244
|
+
`python scripts/check_resources.py --update` and review the catalog diff. This
|
|
245
|
+
updates current-file hashes only; historical source snapshot hashes are retained.
|
|
246
|
+
Keep CLI behavior here and have consumers pin its version; do not maintain a
|
|
247
|
+
second CLI copy in the published plugin.
|
|
248
|
+
|
|
249
|
+
See [ACCEPTANCE.md](ACCEPTANCE.md) for verified scope and outstanding release gates,
|
|
250
|
+
and [the macOS handoff](docs/HANDOFF-MACOS.md) for the remaining acceptance work.
|
|
251
|
+
Report reproducible problems, without keys or private reports, in
|
|
252
|
+
[GitHub Issues](https://github.com/nexscope-ai/nexscope-cli/issues). Nexscope is the
|
|
253
|
+
maintainer; named pilot/release ownership still needs assignment before release.
|
|
254
|
+
|
|
255
|
+
Apache-2.0. Initial source: plugin commit
|
|
256
|
+
`814f28016af6b3ff57ef12093a6bb133c4fb20b9`. Original Skill provenance remains in
|
|
257
|
+
the catalog. See `NOTICE`, `ASSET-NOTICES.md` and `THIRD-PARTY-NOTICES.md`.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
nexscope_cli/__init__.py,sha256=MA6r0Ji2BlUrNJZytOG-Qrf1Ks8M7GIRKUQQwvPQqB4,134
|
|
2
|
+
nexscope_cli/api-contract.json,sha256=DqG4ePGE4rHAF1n8F57dgws6kRC1MdkmQT5oGyAOOEw,1366
|
|
3
|
+
nexscope_cli/catalog.json,sha256=szJeqEps5D1vNIZ5W0pzRBXw78HHwfl_vYiwGao-zxk,3782
|
|
4
|
+
nexscope_cli/dependencies.lock,sha256=Vig5T24dYtoSD89m-AraThIQn0W2gOXWHp0faUlsk_s,23092
|
|
5
|
+
nexscope_cli/dependency-lock.json,sha256=8fvy3PWAjaG9RCa1muu6n2g0_MHKS3Wfe7PELaZp7Gk,22633
|
|
6
|
+
nexscope_cli/doctor.py,sha256=fk3U3KcDjhKuiy88OegBpcM8jJq40ly_1CrKa5NahCM,7885
|
|
7
|
+
nexscope_cli/main.py,sha256=ek6hx-JG8eNOCs-Hec0vaNENaweFrmsysBvOjux5vRc,18549
|
|
8
|
+
nexscope_cli/runner.py,sha256=K3XczQQtUFE_t7JaNrzsnMnQD44S6ktrvdVmL-eYKgg,1712
|
|
9
|
+
nexscope_cli/capabilities/__init__.py,sha256=yC8NklpF_pD51UvpgZXe9V6FAdQ_l7kEdjljgwMlGfQ,147
|
|
10
|
+
nexscope_cli/capabilities/catalog.py,sha256=ColpJeAEFTlmkH5OowOZNSKa83tYPWvl2W95NM3zaS0,4181
|
|
11
|
+
nexscope_cli/capabilities/mock.py,sha256=9GRWHL-dxw3TF5V6GqV4V1RHuBtqTVX60zaP2T1gitE,2486
|
|
12
|
+
nexscope_cli/capabilities/plan.py,sha256=uXPx7Q7KSa-ERYA_mZ2wVS1biRM2SbZn3r4EaV2SxNg,7104
|
|
13
|
+
nexscope_cli/compat/__init__.py,sha256=pZzFzapYd9ZWJYP05f-36a5xrI7YHl1jc0Cyfwy7tHI,156
|
|
14
|
+
nexscope_cli/compat/legacy.py,sha256=WFAiutykjH4MmnzQIb_R2XWkPMOVr4UyZ0WyJaBkYow,6988
|
|
15
|
+
nexscope_cli/core/__init__.py,sha256=G10sB-SuPgPDM-eKwACSR8rRUlRBui-YtDJDtTZ3kHo,114
|
|
16
|
+
nexscope_cli/core/auth.py,sha256=0_wqGcj0CVS5iEnHYV6goJ78W2c9IM1rfx65mUR7alc,3633
|
|
17
|
+
nexscope_cli/core/datasets.py,sha256=4DpQAmkBfOIsWFM2n-CzvK59myi9KTAhIBFiGb2g8P4,18254
|
|
18
|
+
nexscope_cli/core/errors.py,sha256=9x6q2djbY5vD9wyly6Xlj0f0GeCpBCoSp6Q6ZuaNH5E,196
|
|
19
|
+
nexscope_cli/core/evidence.py,sha256=0ZXqJK4btTfrZGLKWydZPKnoenojCYb3FJGUzDtoaxs,5899
|
|
20
|
+
nexscope_cli/core/execution.py,sha256=8gkrxrBTYz3-vV-TIMunnzny_EuMIXZws-B3dbqMZEc,8674
|
|
21
|
+
nexscope_cli/core/remote.py,sha256=_cgdF42GtEQOa1MsLkZxLAY4l37eufUnIUIAjNyjCxo,915
|
|
22
|
+
nexscope_cli/core/transport.py,sha256=EB676UQiQZXu0ar4dnfgDkWVkpwDuosvfQPLqty_geE,2420
|
|
23
|
+
nexscope_cli/reports/__init__.py,sha256=yPZe1M_GvAljCuk7nhnS8c0n1wt53ZJkKNYkBpH-my4,139
|
|
24
|
+
nexscope_cli/reports/compare.py,sha256=vg6KI6bUybgS5ZafLU0X8GCDrCRyIACWwbUl49w5gOw,7195
|
|
25
|
+
nexscope_cli/reports/formats.py,sha256=OQxn7qYOg6T7o0UNF09IfqmDCi7e2N2IYi9_JuTPnBk,5108
|
|
26
|
+
nexscope_cli/resources/__init__.py,sha256=LCYY0xAIWGldghAQQVbtBNTn6OAeHDkyKIR_gHCGk2c,136
|
|
27
|
+
nexscope_cli/resources/fields.json,sha256=VcGNy3fzeGDdKNiDd30qYI8yX_jJNRwc2ykCaytA4Sc,2243
|
|
28
|
+
nexscope_cli/resources/mock-products.json,sha256=0A7bvwm4galLD-giB3m1UtbP9UMb-ugNEXxuCoWBEaU,658
|
|
29
|
+
nexscope_cli/resources/products-sample.csv,sha256=CSNfqSboxonl55xf6qDPOMgABfkyCo5R8k651v_7J_8,511
|
|
30
|
+
nexscope_cli/resources/products-sample.json,sha256=Z2Pp83wA7gFS4lWZGygz2Gyvr4ckDMcOhTnoCVsHH5c,858
|
|
31
|
+
nexscope_cli/resources/products-template.csv,sha256=gBnfKUox-H_2TtRk6sa6lBtZUVvcIWBl8x95o956PDc,100
|
|
32
|
+
nexscope_cli/resources/products.schema.json,sha256=ST7pYiqAX6Nl7B-IPxIO5aXuA6lWkjK-0_FfSwehZU0,4011
|
|
33
|
+
nexscope_cli/resources/report.html,sha256=xrl8ydpn11P3xWd1dUAkUm8OIieGAcEmNVLIF6MZcsk,1710
|
|
34
|
+
nexscope_cli/skills/amazon-product-detail/SKILL.md,sha256=kRmC04bf0sY5NvbjB_Dfxp4Uif8k00IxDL1GABRtF_8,12167
|
|
35
|
+
nexscope_cli/skills/amazon-product-detail/requirements.txt,sha256=8skrREsVeyayEmb00-9vXjJJdxNGmZp9jt0GS9IVw-k,20
|
|
36
|
+
nexscope_cli/skills/amazon-product-detail/references/api.md,sha256=KtU314YeZQoewZV5HnqkiGFBMuEwVHPFOvDHpj9NDVk,10929
|
|
37
|
+
nexscope_cli/skills/amazon-product-detail/scripts/amazon_product_detail.py,sha256=z36uqUhlpufJ3HJ_5eT4AArJGmfIpOTtvmGKyEst5uk,1028
|
|
38
|
+
nexscope_cli/skills/amazon-search/SKILL.md,sha256=kEErz2BwoGmVk7AoUmQh05Z1jx8HBlyJfgNUEXX-cQw,11985
|
|
39
|
+
nexscope_cli/skills/amazon-search/requirements.txt,sha256=8skrREsVeyayEmb00-9vXjJJdxNGmZp9jt0GS9IVw-k,20
|
|
40
|
+
nexscope_cli/skills/amazon-search/references/api.md,sha256=rC9nZN2vvUqw4Zy188_jeDjr12h5xgStYsQKTc1ud-o,8074
|
|
41
|
+
nexscope_cli/skills/amazon-search/scripts/amazon_search.py,sha256=Vp738AVF37pSu11_EjnAjOiqnsMAOuNh3qPD1YW8PC0,1020
|
|
42
|
+
nexscope_cli/workflows/__init__.py,sha256=YeGkzL96b4uUwhBzBHTcN54zdlOQH-YKam00xtCFL1c,136
|
|
43
|
+
nexscope_cli/workflows/compare.py,sha256=Tg2NFZVqBiBufIqZ-hivfND32PA0t5f0CXDL82MkIzs,14835
|
|
44
|
+
nexscope_cli-0.1.0.dist-info/licenses/ASSET-NOTICES.md,sha256=tL-BhhfpHa8I6eIXYaPG4QS2SjJtsjHuptTWl0aBY0Y,449
|
|
45
|
+
nexscope_cli-0.1.0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
|
|
46
|
+
nexscope_cli-0.1.0.dist-info/licenses/NOTICE,sha256=yBCNot83LMzDRtVSqDU0AoTHf8JummN91R3yAmo8lZ4,1053
|
|
47
|
+
nexscope_cli-0.1.0.dist-info/licenses/THIRD-PARTY-NOTICES.md,sha256=xQyGc1Qotvlhsv3guhu_Pcu25OemcQLJ7czVBUqVYGY,2496
|
|
48
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/backports-tarfile/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
|
|
49
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/certifi/LICENSE,sha256=6TcW2mucDVpKHfYP5pWzcPBpVgPSH2-D8FPkLPwQyvc,989
|
|
50
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cffi/LICENSE,sha256=W6JN3FcGf5JJrdZEw6_EGl1tw34jQz73Wdld83Cwr2M,1123
|
|
51
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/charset-normalizer/LICENSE,sha256=bQ1Bv-FwrGx9wkjJpj4lTQ-0WmDVCoJX0K-SxuJJuIc,1071
|
|
52
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cryptography/LICENSE,sha256=Pgx8CRqUi4JTO6mP18u0BDLW8amsv4X1ki0vmak65rs,197
|
|
53
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cryptography/LICENSE.APACHE,sha256=qsc7MUj20dcRHbyjIJn2jSbGRMaBOuHk8F9leaomY_4,11360
|
|
54
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/cryptography/LICENSE.BSD,sha256=YCxMdILeZHndLpeTzaJ15eY9dz2s0eymiSMqtwCPtPs,1532
|
|
55
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/idna/LICENSE.md,sha256=GppPDj1HmickDd1ZqRN6ZqtKD539yMphiMwL_YUYfwQ,1541
|
|
56
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/importlib-metadata/LICENSE,sha256=4ve7BVfno4FJnxW6p8HXdtg9ZhZZkXnXHTEJuOOfeyU,10278
|
|
57
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jaraco-classes/LICENSE,sha256=htoPAa6uRjSKPD1GUZXcHOzN55956HdppkuNoEsqR0E,1023
|
|
58
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jaraco-context/LICENSE,sha256=l1WhhRlmbl8PTK49qtPXASvK5IpgCzEjfXXp_hNOZoM,1076
|
|
59
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jaraco-functools/LICENSE,sha256=l1WhhRlmbl8PTK49qtPXASvK5IpgCzEjfXXp_hNOZoM,1076
|
|
60
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/jeepney/LICENSE,sha256=GyKwSbUmfW38I6Z79KhNjsBLn9-xpR02DkK0NCyLQVQ,1081
|
|
61
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/keyring/LICENSE,sha256=WlfLTbheKi3YjCkGKJCK3VfjRRRJ4KmnH9-zh3b9dZ0,1076
|
|
62
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/more-itertools/LICENSE,sha256=CfHIyelBrz5YTVlkHqm4fYPAyw_QB-te85Gn4mQ8GkY,1053
|
|
63
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/pycparser/LICENSE,sha256=DIRjmTaep23de1xE_m0WSXQV_PAV9cu1CMJL-YuBxbE,1543
|
|
64
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/pywin32-ctypes/LICENSE.txt,sha256=36g7PicJrfzbg42a1VgjymdKu3gOYFY9ndlUTMv3hek,1644
|
|
65
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/requests/LICENSE,sha256=CeipvOyAZxBGUsFoaFqwkx54aPnIKEtm9a5u2uXxEws,10142
|
|
66
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/requests/NOTICE,sha256=9REJct7a0rTp0xRRja87fXLW4C5Jms2AIYHeb3RXHcw,38
|
|
67
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/secretstorage/LICENSE,sha256=5wA1dZK7k3BMsEDOSmZjqFKScWpAQbaiZ_WY8hZYRm8,1504
|
|
68
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/urllib3/LICENSE.txt,sha256=Ew46ZNX91dCWp1JpRjSn2d8oRGnehuVzIQAmgEHj1oY,1093
|
|
69
|
+
nexscope_cli-0.1.0.dist-info/licenses/third-party-notices/zipp/LICENSE,sha256=l1WhhRlmbl8PTK49qtPXASvK5IpgCzEjfXXp_hNOZoM,1076
|
|
70
|
+
nexscope_cli-0.1.0.dist-info/METADATA,sha256=L-4MVoenH7TzK4Q7Son7iGay7-LRjfgkTC8ltmfP7Ig,14309
|
|
71
|
+
nexscope_cli-0.1.0.dist-info/WHEEL,sha256=mOdXR9C3wMVov7cSY8PY-XCzo3SmmWCHvGQr5csJoao,91
|
|
72
|
+
nexscope_cli-0.1.0.dist-info/entry_points.txt,sha256=izkHi1XuzJJ-NnMB-21B4fT7aP7Q6W-qS8dhYW4FUDU,52
|
|
73
|
+
nexscope_cli-0.1.0.dist-info/top_level.txt,sha256=nayB2rke499IWWFGp9mH-63cKyCrz5VG_bAOslgOsUE,13
|
|
74
|
+
nexscope_cli-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Bundled asset attribution
|
|
2
|
+
|
|
3
|
+
Copyright 2026 Nexscope.
|
|
4
|
+
|
|
5
|
+
This independent CLI contains report templates and fictional product samples.
|
|
6
|
+
Templates use system fonts, with no external font, JavaScript or image assets.
|
|
7
|
+
The original plugin icon and plugin entrypoint are not part of this distribution.
|
|
8
|
+
Apache-2.0 Section 6 governs trademark use; the copyright license does not grant
|
|
9
|
+
trademark rights or imply Nexscope endorsement of modified distributions.
|