@simonfestl/husky-cli 0.8.2 → 0.9.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 +3 -116
- package/dist/commands/biz/customers.d.ts +8 -0
- package/dist/commands/biz/customers.js +181 -0
- package/dist/commands/biz/orders.d.ts +8 -0
- package/dist/commands/biz/orders.js +226 -0
- package/dist/commands/biz/products.d.ts +8 -0
- package/dist/commands/biz/products.js +255 -0
- package/dist/commands/biz/qdrant.d.ts +8 -0
- package/dist/commands/biz/qdrant.js +170 -0
- package/dist/commands/biz/seatable.d.ts +8 -0
- package/dist/commands/biz/seatable.js +449 -0
- package/dist/commands/biz/tickets.d.ts +8 -0
- package/dist/commands/biz/tickets.js +600 -0
- package/dist/commands/biz.d.ts +9 -0
- package/dist/commands/biz.js +22 -0
- package/dist/commands/config.d.ts +13 -0
- package/dist/commands/config.js +43 -16
- package/dist/commands/explain.js +12 -595
- package/dist/commands/idea.js +2 -50
- package/dist/commands/project.js +2 -47
- package/dist/commands/roadmap.js +0 -107
- package/dist/commands/task.js +11 -17
- package/dist/commands/vm.js +0 -225
- package/dist/commands/workflow.js +4 -60
- package/dist/index.js +5 -1
- package/dist/lib/biz/billbee-types.d.ts +259 -0
- package/dist/lib/biz/billbee-types.js +41 -0
- package/dist/lib/biz/billbee.d.ts +37 -0
- package/dist/lib/biz/billbee.js +165 -0
- package/dist/lib/biz/embeddings.d.ts +45 -0
- package/dist/lib/biz/embeddings.js +115 -0
- package/dist/lib/biz/index.d.ts +13 -0
- package/dist/lib/biz/index.js +11 -0
- package/dist/lib/biz/qdrant.d.ts +52 -0
- package/dist/lib/biz/qdrant.js +158 -0
- package/dist/lib/biz/seatable-types.d.ts +115 -0
- package/dist/lib/biz/seatable-types.js +27 -0
- package/dist/lib/biz/seatable.d.ts +49 -0
- package/dist/lib/biz/seatable.js +210 -0
- package/dist/lib/biz/zendesk-types.d.ts +136 -0
- package/dist/lib/biz/zendesk-types.js +28 -0
- package/dist/lib/biz/zendesk.d.ts +45 -0
- package/dist/lib/biz/zendesk.js +206 -0
- package/package.json +2 -2
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Husky Biz SeaTable Command
|
|
3
|
+
*
|
|
4
|
+
* Manages SeaTable Base data for Supply Chain workflows
|
|
5
|
+
*/
|
|
6
|
+
import { Command } from "commander";
|
|
7
|
+
import { SeaTableClient } from "../../lib/biz/index.js";
|
|
8
|
+
export const seatableCommand = new Command("seatable")
|
|
9
|
+
.description("Manage SeaTable data (Supply Chain)");
|
|
10
|
+
// husky biz seatable tables
|
|
11
|
+
seatableCommand
|
|
12
|
+
.command("tables")
|
|
13
|
+
.description("List all tables in the Base")
|
|
14
|
+
.option("--json", "Output as JSON")
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
try {
|
|
17
|
+
const client = SeaTableClient.fromConfig();
|
|
18
|
+
const metadata = await client.getMetadata();
|
|
19
|
+
if (options.json) {
|
|
20
|
+
console.log(JSON.stringify(metadata.tables, null, 2));
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
console.log(`\n 📊 SeaTable Base Tables (${metadata.tables.length})\n`);
|
|
24
|
+
for (const table of metadata.tables) {
|
|
25
|
+
console.log(` 📋 ${table.name}`);
|
|
26
|
+
console.log(` ID: ${table._id} | Columns: ${table.columns.length}`);
|
|
27
|
+
}
|
|
28
|
+
console.log("");
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
console.error("Error:", error.message);
|
|
32
|
+
process.exit(1);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
// husky biz seatable columns <table>
|
|
36
|
+
seatableCommand
|
|
37
|
+
.command("columns <table>")
|
|
38
|
+
.description("Show columns for a table")
|
|
39
|
+
.option("--json", "Output as JSON")
|
|
40
|
+
.action(async (tableName, options) => {
|
|
41
|
+
try {
|
|
42
|
+
const client = SeaTableClient.fromConfig();
|
|
43
|
+
const metadata = await client.getMetadata();
|
|
44
|
+
const table = metadata.tables.find(t => t.name === tableName);
|
|
45
|
+
if (!table) {
|
|
46
|
+
console.error(`Table "${tableName}" not found.`);
|
|
47
|
+
console.log("Available tables:", metadata.tables.map(t => t.name).join(", "));
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
if (options.json) {
|
|
51
|
+
console.log(JSON.stringify(table.columns, null, 2));
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log(`\n 📋 Columns in "${tableName}"\n`);
|
|
55
|
+
for (const col of table.columns) {
|
|
56
|
+
const typeStr = String(col.type).padEnd(15);
|
|
57
|
+
console.log(` ${col.name.padEnd(25)} │ ${typeStr} │ ${col.key}`);
|
|
58
|
+
}
|
|
59
|
+
console.log("");
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
console.error("Error:", error.message);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// husky biz seatable list <table>
|
|
67
|
+
seatableCommand
|
|
68
|
+
.command("list <table>")
|
|
69
|
+
.description("List rows from a table")
|
|
70
|
+
.option("-l, --limit <num>", "Number of rows", "20")
|
|
71
|
+
.option("--start <num>", "Start offset", "0")
|
|
72
|
+
.option("--order-by <col>", "Order by column")
|
|
73
|
+
.option("--desc", "Descending order")
|
|
74
|
+
.option("-v, --view <name>", "View name")
|
|
75
|
+
.option("--json", "Output as JSON")
|
|
76
|
+
.action(async (tableName, options) => {
|
|
77
|
+
try {
|
|
78
|
+
const client = SeaTableClient.fromConfig();
|
|
79
|
+
const rows = await client.listRows({
|
|
80
|
+
table_name: tableName,
|
|
81
|
+
limit: parseInt(options.limit, 10),
|
|
82
|
+
start: parseInt(options.start, 10),
|
|
83
|
+
order_by: options.orderBy,
|
|
84
|
+
direction: options.desc ? 'desc' : undefined,
|
|
85
|
+
view_name: options.view,
|
|
86
|
+
});
|
|
87
|
+
if (options.json) {
|
|
88
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
console.log(`\n 📋 ${tableName} (${rows.length} rows)\n`);
|
|
92
|
+
if (rows.length === 0) {
|
|
93
|
+
console.log(" No rows found.");
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Display first few columns of each row
|
|
97
|
+
for (const row of rows) {
|
|
98
|
+
const displayCols = Object.entries(row)
|
|
99
|
+
.filter(([k]) => !k.startsWith('_'))
|
|
100
|
+
.slice(0, 4)
|
|
101
|
+
.map(([k, v]) => `${k}: ${formatValue(v)}`)
|
|
102
|
+
.join(' │ ');
|
|
103
|
+
console.log(` [${row._id.slice(0, 8)}] ${displayCols}`);
|
|
104
|
+
}
|
|
105
|
+
console.log("");
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
console.error("Error:", error.message);
|
|
109
|
+
process.exit(1);
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
// husky biz seatable get <table> <rowId>
|
|
113
|
+
seatableCommand
|
|
114
|
+
.command("get <table> <rowId>")
|
|
115
|
+
.description("Get a single row by ID")
|
|
116
|
+
.option("--json", "Output as JSON")
|
|
117
|
+
.action(async (tableName, rowId, options) => {
|
|
118
|
+
try {
|
|
119
|
+
const client = SeaTableClient.fromConfig();
|
|
120
|
+
const row = await client.getRow(tableName, rowId);
|
|
121
|
+
if (!row) {
|
|
122
|
+
console.error(`Row "${rowId}" not found in table "${tableName}".`);
|
|
123
|
+
process.exit(1);
|
|
124
|
+
}
|
|
125
|
+
if (options.json) {
|
|
126
|
+
console.log(JSON.stringify(row, null, 2));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(`\n 📋 Row ${rowId}\n`);
|
|
130
|
+
for (const [key, value] of Object.entries(row)) {
|
|
131
|
+
if (!key.startsWith('_')) {
|
|
132
|
+
console.log(` ${key.padEnd(20)}: ${formatValue(value)}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
console.log(`\n Created: ${row._ctime || 'N/A'}`);
|
|
136
|
+
console.log(` Modified: ${row._mtime || 'N/A'}\n`);
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
console.error("Error:", error.message);
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
// husky biz seatable search <table> <query>
|
|
144
|
+
seatableCommand
|
|
145
|
+
.command("search <table> <query>")
|
|
146
|
+
.description("Full-text search in a table")
|
|
147
|
+
.option("--json", "Output as JSON")
|
|
148
|
+
.action(async (tableName, query, options) => {
|
|
149
|
+
try {
|
|
150
|
+
const client = SeaTableClient.fromConfig();
|
|
151
|
+
const rows = await client.searchRows(tableName, query);
|
|
152
|
+
if (options.json) {
|
|
153
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
console.log(`\n 🔍 Search "${query}" in ${tableName} (${rows.length} results)\n`);
|
|
157
|
+
if (rows.length === 0) {
|
|
158
|
+
console.log(" No matching rows found.");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
for (const row of rows) {
|
|
162
|
+
const displayCols = Object.entries(row)
|
|
163
|
+
.filter(([k]) => !k.startsWith('_'))
|
|
164
|
+
.slice(0, 4)
|
|
165
|
+
.map(([k, v]) => `${k}: ${formatValue(v)}`)
|
|
166
|
+
.join(' │ ');
|
|
167
|
+
console.log(` [${row._id.slice(0, 8)}] ${displayCols}`);
|
|
168
|
+
}
|
|
169
|
+
console.log("");
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
console.error("Error:", error.message);
|
|
173
|
+
process.exit(1);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
// husky biz seatable create <table>
|
|
177
|
+
seatableCommand
|
|
178
|
+
.command("create <table>")
|
|
179
|
+
.description("Create a new row")
|
|
180
|
+
.requiredOption("-d, --data <json>", "Row data as JSON")
|
|
181
|
+
.option("--json", "Output created row as JSON")
|
|
182
|
+
.action(async (tableName, options) => {
|
|
183
|
+
try {
|
|
184
|
+
const client = SeaTableClient.fromConfig();
|
|
185
|
+
let data;
|
|
186
|
+
try {
|
|
187
|
+
data = JSON.parse(options.data);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
console.error("Error: Invalid JSON data");
|
|
191
|
+
console.log('Example: --data \'{"Name":"Test","Status":"Active"}\'');
|
|
192
|
+
process.exit(1);
|
|
193
|
+
}
|
|
194
|
+
const row = await client.appendRow(tableName, data);
|
|
195
|
+
if (options.json) {
|
|
196
|
+
console.log(JSON.stringify(row, null, 2));
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
console.log(`✓ Created row in ${tableName}`);
|
|
200
|
+
console.log(` ID: ${row._id}`);
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
console.error("Error:", error.message);
|
|
204
|
+
process.exit(1);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
// husky biz seatable update <table> <rowId>
|
|
208
|
+
seatableCommand
|
|
209
|
+
.command("update <table> <rowId>")
|
|
210
|
+
.description("Update a row")
|
|
211
|
+
.requiredOption("-d, --data <json>", "Update data as JSON")
|
|
212
|
+
.action(async (tableName, rowId, options) => {
|
|
213
|
+
try {
|
|
214
|
+
const client = SeaTableClient.fromConfig();
|
|
215
|
+
let data;
|
|
216
|
+
try {
|
|
217
|
+
data = JSON.parse(options.data);
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
console.error("Error: Invalid JSON data");
|
|
221
|
+
console.log('Example: --data \'{"Status":"Completed"}\'');
|
|
222
|
+
process.exit(1);
|
|
223
|
+
}
|
|
224
|
+
await client.updateRow(tableName, rowId, data);
|
|
225
|
+
console.log(`✓ Updated row ${rowId} in ${tableName}`);
|
|
226
|
+
}
|
|
227
|
+
catch (error) {
|
|
228
|
+
console.error("Error:", error.message);
|
|
229
|
+
process.exit(1);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
// husky biz seatable delete <table> <rowId>
|
|
233
|
+
seatableCommand
|
|
234
|
+
.command("delete <table> <rowId>")
|
|
235
|
+
.description("Delete a row")
|
|
236
|
+
.option("-f, --force", "Skip confirmation")
|
|
237
|
+
.action(async (tableName, rowId, options) => {
|
|
238
|
+
try {
|
|
239
|
+
const client = SeaTableClient.fromConfig();
|
|
240
|
+
if (!options.force) {
|
|
241
|
+
console.log(`⚠️ About to delete row ${rowId} from ${tableName}`);
|
|
242
|
+
console.log(" Use --force to skip this confirmation");
|
|
243
|
+
process.exit(0);
|
|
244
|
+
}
|
|
245
|
+
await client.deleteRow(tableName, rowId);
|
|
246
|
+
console.log(`✓ Deleted row ${rowId} from ${tableName}`);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
console.error("Error:", error.message);
|
|
250
|
+
process.exit(1);
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
// Helper: format value for display
|
|
254
|
+
function formatValue(value) {
|
|
255
|
+
if (value === null || value === undefined)
|
|
256
|
+
return '-';
|
|
257
|
+
if (typeof value === 'string')
|
|
258
|
+
return value.slice(0, 40) + (value.length > 40 ? '...' : '');
|
|
259
|
+
if (typeof value === 'number')
|
|
260
|
+
return String(value);
|
|
261
|
+
if (typeof value === 'boolean')
|
|
262
|
+
return value ? '✓' : '✗';
|
|
263
|
+
if (Array.isArray(value))
|
|
264
|
+
return `[${value.length} items]`;
|
|
265
|
+
if (typeof value === 'object')
|
|
266
|
+
return '[object]';
|
|
267
|
+
return String(value);
|
|
268
|
+
}
|
|
269
|
+
// ============================================================================
|
|
270
|
+
// PREBUILD COMMANDS (Agent-Focused)
|
|
271
|
+
// ============================================================================
|
|
272
|
+
import { EmbeddingService, QdrantClient } from "../../lib/biz/index.js";
|
|
273
|
+
const PRODUCTS_COLLECTION = "supplychain_nocodb_products";
|
|
274
|
+
// husky biz seatable find-product "<query>"
|
|
275
|
+
seatableCommand
|
|
276
|
+
.command("find-product <query>")
|
|
277
|
+
.description("[PREBUILD] Semantic search for products")
|
|
278
|
+
.option("-l, --limit <num>", "Number of results", "5")
|
|
279
|
+
.option("--json", "Output as JSON")
|
|
280
|
+
.action(async (query, options) => {
|
|
281
|
+
try {
|
|
282
|
+
const embeddings = EmbeddingService.fromConfig();
|
|
283
|
+
const qdrant = QdrantClient.fromConfig();
|
|
284
|
+
// 1. Create embedding from query
|
|
285
|
+
console.log(` Generating embedding for "${query}"...`);
|
|
286
|
+
const vector = await embeddings.embed(query);
|
|
287
|
+
// 2. Search Qdrant products collection (uses named vector 'product_name')
|
|
288
|
+
console.log(` Searching ${PRODUCTS_COLLECTION}...`);
|
|
289
|
+
const results = await qdrant.search(PRODUCTS_COLLECTION, vector, parseInt(options.limit, 10), { vectorName: 'product_name' });
|
|
290
|
+
if (options.json) {
|
|
291
|
+
console.log(JSON.stringify({
|
|
292
|
+
success: true,
|
|
293
|
+
query,
|
|
294
|
+
products: results.map(r => ({
|
|
295
|
+
id: r.id,
|
|
296
|
+
score: r.score,
|
|
297
|
+
...r.payload,
|
|
298
|
+
})),
|
|
299
|
+
}, null, 2));
|
|
300
|
+
return;
|
|
301
|
+
}
|
|
302
|
+
console.log(`\n 🔍 Product search: "${query}"\n`);
|
|
303
|
+
if (results.length === 0) {
|
|
304
|
+
console.log(" No products found.");
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
for (const result of results) {
|
|
308
|
+
const payload = result.payload;
|
|
309
|
+
const sku = payload?.sku || payload?.SKU || '';
|
|
310
|
+
const name = payload?.name || payload?.Name || payload?.title || '';
|
|
311
|
+
console.log(` [${(result.score * 100).toFixed(1)}%] ${sku} │ ${String(name).slice(0, 50)}`);
|
|
312
|
+
}
|
|
313
|
+
console.log("");
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
console.error("Error:", error.message);
|
|
317
|
+
process.exit(1);
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
// husky biz seatable find-supplier "<query>"
|
|
321
|
+
seatableCommand
|
|
322
|
+
.command("find-supplier <query>")
|
|
323
|
+
.description("[PREBUILD] Text search for suppliers")
|
|
324
|
+
.option("-l, --limit <num>", "Number of results", "10")
|
|
325
|
+
.option("--json", "Output as JSON")
|
|
326
|
+
.action(async (query, options) => {
|
|
327
|
+
try {
|
|
328
|
+
const client = SeaTableClient.fromConfig();
|
|
329
|
+
// List all suppliers and filter client-side
|
|
330
|
+
console.log(` Searching Suppliers for "${query}"...`);
|
|
331
|
+
const allRows = await client.listRows({ table_name: "Suppliers", limit: 100 });
|
|
332
|
+
const lowerQuery = query.toLowerCase();
|
|
333
|
+
const filtered = allRows.filter(row => {
|
|
334
|
+
const name = String(row.Name || row['0000'] || '').toLowerCase();
|
|
335
|
+
const tag = String(row.TAG || '').toLowerCase();
|
|
336
|
+
return name.includes(lowerQuery) || tag.includes(lowerQuery);
|
|
337
|
+
});
|
|
338
|
+
const limitedRows = filtered.slice(0, parseInt(options.limit, 10));
|
|
339
|
+
if (options.json) {
|
|
340
|
+
console.log(JSON.stringify({ success: true, query, suppliers: limitedRows }, null, 2));
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
console.log(`\n 🔍 Supplier search: "${query}" (${limitedRows.length} results)\n`);
|
|
344
|
+
if (limitedRows.length === 0) {
|
|
345
|
+
console.log(" No suppliers found.");
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
for (const row of limitedRows) {
|
|
349
|
+
const name = row.Name || row['0000'] || '';
|
|
350
|
+
const tag = row.TAG || '';
|
|
351
|
+
const country = row.Country || '';
|
|
352
|
+
console.log(` [${row._id.slice(0, 8)}] ${name} │ ${tag} │ ${country}`);
|
|
353
|
+
}
|
|
354
|
+
console.log("");
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
console.error("Error:", error.message);
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
});
|
|
361
|
+
// husky biz seatable order-status <orderNo>
|
|
362
|
+
seatableCommand
|
|
363
|
+
.command("order-status <orderNo>")
|
|
364
|
+
.description("[PREBUILD] Get order with all positions")
|
|
365
|
+
.option("--json", "Output as JSON")
|
|
366
|
+
.action(async (orderNo, options) => {
|
|
367
|
+
try {
|
|
368
|
+
const client = SeaTableClient.fromConfig();
|
|
369
|
+
// 1. Find order in Orders table
|
|
370
|
+
console.log(` Looking up order ${orderNo}...`);
|
|
371
|
+
const orders = await client.searchRows("Orders", orderNo);
|
|
372
|
+
if (orders.length === 0) {
|
|
373
|
+
console.error(`Order "${orderNo}" not found.`);
|
|
374
|
+
process.exit(1);
|
|
375
|
+
}
|
|
376
|
+
const order = orders[0];
|
|
377
|
+
// 2. Get order positions
|
|
378
|
+
console.log(` Fetching order positions...`);
|
|
379
|
+
let positions = [];
|
|
380
|
+
try {
|
|
381
|
+
positions = await client.searchRows("Order Positions", orderNo);
|
|
382
|
+
}
|
|
383
|
+
catch {
|
|
384
|
+
// Table might not exist or be empty
|
|
385
|
+
}
|
|
386
|
+
if (options.json) {
|
|
387
|
+
console.log(JSON.stringify({ success: true, order, positions }, null, 2));
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
console.log(`\n 📦 Order: ${orderNo}`);
|
|
391
|
+
console.log(" " + "─".repeat(50));
|
|
392
|
+
// Display order fields
|
|
393
|
+
for (const [key, value] of Object.entries(order)) {
|
|
394
|
+
if (!key.startsWith('_')) {
|
|
395
|
+
console.log(` ${key}: ${formatValue(value)}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
if (positions.length > 0) {
|
|
399
|
+
console.log(`\n 📋 Positions (${positions.length}):`);
|
|
400
|
+
console.log(" " + "─".repeat(50));
|
|
401
|
+
for (const pos of positions) {
|
|
402
|
+
const sku = pos.SKU || pos.sku || '';
|
|
403
|
+
const name = pos.ProductName || pos.name || '';
|
|
404
|
+
const qty = pos.Qty || pos.qty || '';
|
|
405
|
+
console.log(` ${sku} │ ${qty}x │ ${String(name).slice(0, 35)}`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
console.log("");
|
|
409
|
+
}
|
|
410
|
+
catch (error) {
|
|
411
|
+
console.error("Error:", error.message);
|
|
412
|
+
process.exit(1);
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
// husky biz seatable stock-check <sku>
|
|
416
|
+
seatableCommand
|
|
417
|
+
.command("stock-check <sku>")
|
|
418
|
+
.description("[PREBUILD] Check stock across suppliers")
|
|
419
|
+
.option("--json", "Output as JSON")
|
|
420
|
+
.action(async (sku, options) => {
|
|
421
|
+
try {
|
|
422
|
+
const client = SeaTableClient.fromConfig();
|
|
423
|
+
// Search Supplier_Sources for this SKU
|
|
424
|
+
console.log(` Checking suppliers for SKU "${sku}"...`);
|
|
425
|
+
const sources = await client.searchRows("Supplier_Sources", sku);
|
|
426
|
+
if (options.json) {
|
|
427
|
+
console.log(JSON.stringify({ success: true, sku, sources }, null, 2));
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
console.log(`\n 📦 Stock check: ${sku}\n`);
|
|
431
|
+
if (sources.length === 0) {
|
|
432
|
+
console.log(" No supplier sources found for this SKU.");
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
for (const source of sources) {
|
|
436
|
+
const supplier = source.Supplier || source.supplier_name || '';
|
|
437
|
+
const price = source.Price || source.price || '-';
|
|
438
|
+
const stock = source.Stock || source.stock || source.available || '-';
|
|
439
|
+
const leadTime = source.LeadTime || source['Delivery Time'] || '-';
|
|
440
|
+
console.log(` ${String(supplier).padEnd(20)} │ ${String(price).padEnd(8)} │ Stock: ${stock} │ Lead: ${leadTime}d`);
|
|
441
|
+
}
|
|
442
|
+
console.log("");
|
|
443
|
+
}
|
|
444
|
+
catch (error) {
|
|
445
|
+
console.error("Error:", error.message);
|
|
446
|
+
process.exit(1);
|
|
447
|
+
}
|
|
448
|
+
});
|
|
449
|
+
export default seatableCommand;
|