@maptec/cli 1.0.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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +284 -0
  3. package/bin/maptec.js +2 -0
  4. package/dist/chunk-K574OFFK.js +122 -0
  5. package/dist/chunk-K574OFFK.js.map +1 -0
  6. package/dist/cli.js +899 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/store-ZU7HLXCV.js +18 -0
  9. package/dist/store-ZU7HLXCV.js.map +1 -0
  10. package/package.json +47 -0
  11. package/skills/README.md +30 -0
  12. package/skills/jsapi-skills/README.md +60 -0
  13. package/skills/jsapi-skills/SKILL.md +145 -0
  14. package/skills/jsapi-skills/package.json +31 -0
  15. package/skills/jsapi-skills/references/controls.md +195 -0
  16. package/skills/jsapi-skills/references/events.md +222 -0
  17. package/skills/jsapi-skills/references/geocoding.md +184 -0
  18. package/skills/jsapi-skills/references/map-style.md +123 -0
  19. package/skills/jsapi-skills/references/map.md +154 -0
  20. package/skills/jsapi-skills/references/marker.md +136 -0
  21. package/skills/jsapi-skills/references/operate.md +138 -0
  22. package/skills/jsapi-skills/references/overlay.md +311 -0
  23. package/skills/jsapi-skills/references/place-search.md +411 -0
  24. package/skills/jsapi-skills/references/poi-categories.md +123 -0
  25. package/skills/jsapi-skills/references/popup.md +146 -0
  26. package/skills/jsapi-skills/references/re-geocoding.md +183 -0
  27. package/skills/jsapi-skills/references/routing.md +347 -0
  28. package/skills/jsapi-skills/references/track.md +240 -0
  29. package/skills/jsapi-skills/scripts/validate_jsapi_skill.py +160 -0
  30. package/skills/webapi-skills/README.md +73 -0
  31. package/skills/webapi-skills/SKILL.md +63 -0
  32. package/skills/webapi-skills/package.json +32 -0
  33. package/skills/webapi-skills/references/direction-api.md +175 -0
  34. package/skills/webapi-skills/references/geocoding.md +195 -0
  35. package/skills/webapi-skills/references/ip-location.md +87 -0
  36. package/skills/webapi-skills/references/matrix-api.md +149 -0
  37. package/skills/webapi-skills/references/nearby-search.md +223 -0
  38. package/skills/webapi-skills/references/places.md +239 -0
  39. package/skills/webapi-skills/references/quick-start-cn.md +219 -0
  40. package/skills/webapi-skills/references/reverse-geocoding.md +157 -0
  41. package/skills/webapi-skills/references/suggest.md +111 -0
  42. package/skills/webapi-skills/references/text-search.md +282 -0
  43. package/skills/webapi-skills/scripts/validate_webapi_skill.py +158 -0
package/dist/cli.js ADDED
@@ -0,0 +1,899 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ API_VERSION,
4
+ getConfigPath,
5
+ loadConfig,
6
+ maskApiKey,
7
+ normalizeBaseUrl,
8
+ resolveApiKey,
9
+ resolveBaseUrl,
10
+ saveConfig,
11
+ setApiKey,
12
+ setBaseUrl,
13
+ setConfigValue
14
+ } from "./chunk-K574OFFK.js";
15
+
16
+ // src/cli.ts
17
+ import { Command } from "commander";
18
+ import { readFileSync } from "fs";
19
+ import { fileURLToPath } from "url";
20
+ import { dirname, join } from "path";
21
+
22
+ // src/errors.ts
23
+ var ExitCode = {
24
+ USER_ERROR: 1,
25
+ CONFIG_MISSING: 2,
26
+ API_ERROR: 3,
27
+ NETWORK_ERROR: 4,
28
+ INTERNAL_ERROR: 5
29
+ };
30
+ var CliError = class extends Error {
31
+ constructor(message, exitCode, hint) {
32
+ super(message);
33
+ this.exitCode = exitCode;
34
+ this.hint = hint;
35
+ this.name = "CliError";
36
+ }
37
+ exitCode;
38
+ hint;
39
+ };
40
+ function printError(err) {
41
+ if (err instanceof CliError) {
42
+ console.error(`error: ${err.message}`);
43
+ if (err.hint) {
44
+ console.error(`hint: ${err.hint}`);
45
+ }
46
+ return;
47
+ }
48
+ if (err instanceof Error) {
49
+ console.error(`error: ${err.message}`);
50
+ return;
51
+ }
52
+ console.error("error: \u672A\u77E5\u9519\u8BEF");
53
+ }
54
+ function exitWithError(err) {
55
+ printError(err);
56
+ const code = err instanceof CliError ? err.exitCode : ExitCode.INTERNAL_ERROR;
57
+ process.exit(code);
58
+ }
59
+
60
+ // src/commands/init.ts
61
+ import * as readline from "readline/promises";
62
+ import { stdin as input, stdout as output } from "process";
63
+
64
+ // src/output/json.ts
65
+ import { writeFile } from "fs/promises";
66
+
67
+ // src/output/fields.ts
68
+ function pickFields(data, fields) {
69
+ if (data === null || data === void 0) return data;
70
+ if (Array.isArray(data)) {
71
+ return data.map((item) => pickFields(item, fields));
72
+ }
73
+ if (typeof data !== "object") return data;
74
+ const fieldSet = new Set(fields.map((f) => f.trim()).filter(Boolean));
75
+ if (fieldSet.size === 0) return data;
76
+ const out = {};
77
+ for (const key of fieldSet) {
78
+ if (key in data) {
79
+ out[key] = data[key];
80
+ }
81
+ }
82
+ return out;
83
+ }
84
+
85
+ // src/output/json.ts
86
+ async function emitSuccess(payload, options) {
87
+ let data = payload.data;
88
+ if (options.fields?.length) {
89
+ data = pickFields(data, options.fields);
90
+ }
91
+ const envelope = {
92
+ ok: true,
93
+ command: payload.command,
94
+ data
95
+ };
96
+ if (options.format === "table") {
97
+ const text = formatTable(data);
98
+ await writeOutput(text, options);
99
+ return;
100
+ }
101
+ const json = `${JSON.stringify(envelope, null, 2)}
102
+ `;
103
+ await writeOutput(json, options);
104
+ }
105
+ async function writeOutput(text, options) {
106
+ if (options.out) {
107
+ await writeFile(options.out, text, "utf8");
108
+ if (!options.quiet) {
109
+ console.error(`\u5DF2\u5199\u5165 ${options.out}`);
110
+ }
111
+ return;
112
+ }
113
+ process.stdout.write(text.endsWith("\n") ? text : `${text}
114
+ `);
115
+ }
116
+ function formatTable(data) {
117
+ if (data === null || data === void 0) {
118
+ return "(empty)";
119
+ }
120
+ if (typeof data !== "object") {
121
+ return String(data);
122
+ }
123
+ if (Array.isArray(data)) {
124
+ if (data.length === 0) return "(no results)";
125
+ return data.map((item, i) => `[${i}] ${JSON.stringify(item)}`).join("\n");
126
+ }
127
+ return Object.entries(data).map(([k, v]) => `${k}: ${typeof v === "object" ? JSON.stringify(v) : String(v)}`).join("\n");
128
+ }
129
+ function handleApiBusinessStatus(result, command) {
130
+ if (result.status === "ERROR" && result.error) {
131
+ const err = result.error;
132
+ throw new CliError(
133
+ `${err.code} \u2014 ${err.message}`,
134
+ ExitCode.API_ERROR,
135
+ `\u68C0\u67E5\u547D\u4EE4\u53C2\u6570\uFF0C\u5F53\u524D\u547D\u4EE4\uFF1A${command}`
136
+ );
137
+ }
138
+ return result;
139
+ }
140
+
141
+ // src/commands/init.ts
142
+ async function runInit(outputOpts) {
143
+ const rl = readline.createInterface({ input, output });
144
+ try {
145
+ const existing = await loadConfig();
146
+ const apiKeyPrompt = existing.apiKey ? `API Key [\u5F53\u524D ${maskApiKey(existing.apiKey)}\uFF0C\u56DE\u8F66\u4FDD\u7559]: ` : "API Key: ";
147
+ const apiKeyInput = (await rl.question(apiKeyPrompt)).trim();
148
+ const baseUrlPrompt = existing.baseUrl ? `Base URL [\u5F53\u524D ${existing.baseUrl}\uFF0C\u56DE\u8F66\u4FDD\u7559]: ` : "Base URL (https://api.example.com): ";
149
+ const baseUrlInput = (await rl.question(baseUrlPrompt)).trim();
150
+ const config = await loadConfig();
151
+ if (apiKeyInput) {
152
+ config.apiKey = apiKeyInput;
153
+ }
154
+ if (!config.apiKey) {
155
+ throw new CliError(
156
+ "\u5FC5\u987B\u63D0\u4F9B API Key",
157
+ ExitCode.USER_ERROR,
158
+ "\u4ECE Maptec \u5F00\u653E\u5E73\u53F0\u63A7\u5236\u53F0\u83B7\u53D6\uFF0C\u6216\u8BBE\u7F6E MAPTEC_API_KEY"
159
+ );
160
+ }
161
+ if (baseUrlInput) {
162
+ try {
163
+ config.baseUrl = normalizeBaseUrl(baseUrlInput);
164
+ } catch (err) {
165
+ throw new CliError(
166
+ err instanceof Error ? err.message : "\u65E0\u6548 Base URL",
167
+ ExitCode.USER_ERROR
168
+ );
169
+ }
170
+ }
171
+ if (!config.baseUrl) {
172
+ throw new CliError(
173
+ "\u5FC5\u987B\u63D0\u4F9B Base URL",
174
+ ExitCode.USER_ERROR,
175
+ "\u793A\u4F8B\uFF1Ahttps://api.maptec.com \uFF0C\u6216\u8BBE\u7F6E MAPTEC_BASE_URL"
176
+ );
177
+ }
178
+ delete config.env;
179
+ await saveConfig(config);
180
+ await emitSuccess(
181
+ {
182
+ command: "init",
183
+ data: {
184
+ apiKey: maskApiKey(config.apiKey),
185
+ baseUrl: config.baseUrl,
186
+ configPath: "~/.maptec/config.json"
187
+ }
188
+ },
189
+ outputOpts
190
+ );
191
+ } finally {
192
+ rl.close();
193
+ }
194
+ }
195
+
196
+ // src/commands/config.ts
197
+ async function runConfigGet(outputOpts) {
198
+ const config = await loadConfig();
199
+ const apiKey = resolveApiKey(config);
200
+ await emitSuccess(
201
+ {
202
+ command: "config.get",
203
+ data: {
204
+ apiKey: apiKey ? maskApiKey(apiKey) : null,
205
+ baseUrl: resolveBaseUrl(config) ?? null,
206
+ defaultFormat: config.defaultFormat,
207
+ timeoutMs: config.timeoutMs,
208
+ configPath: getConfigPath()
209
+ }
210
+ },
211
+ outputOpts
212
+ );
213
+ }
214
+ async function runConfigSet(key, value, outputOpts) {
215
+ let config;
216
+ switch (key) {
217
+ case "api-key":
218
+ case "apiKey":
219
+ config = await setApiKey(value);
220
+ break;
221
+ case "base-url":
222
+ case "baseUrl":
223
+ try {
224
+ config = await setBaseUrl(value);
225
+ } catch (err) {
226
+ throw new CliError(
227
+ err instanceof Error ? err.message : "\u65E0\u6548 baseUrl",
228
+ ExitCode.USER_ERROR
229
+ );
230
+ }
231
+ break;
232
+ case "default-format":
233
+ case "defaultFormat":
234
+ config = await setConfigValue("defaultFormat", value);
235
+ break;
236
+ case "timeout":
237
+ case "timeoutMs":
238
+ config = await setConfigValue("timeoutMs", Number(value));
239
+ break;
240
+ default:
241
+ throw new CliError(
242
+ `\u672A\u77E5\u914D\u7F6E\u9879 "${key}"`,
243
+ ExitCode.USER_ERROR,
244
+ "\u53EF\u9009\uFF1Aapi-key | base-url | default-format | timeout"
245
+ );
246
+ }
247
+ await emitSuccess(
248
+ {
249
+ command: "config.set",
250
+ data: {
251
+ key,
252
+ baseUrl: config.baseUrl ?? null,
253
+ apiKey: config.apiKey ? maskApiKey(config.apiKey) : null
254
+ }
255
+ },
256
+ outputOpts
257
+ );
258
+ }
259
+
260
+ // src/api/client.ts
261
+ function buildQueryString(query) {
262
+ const parts = Object.entries(query).filter(([, v]) => v !== void 0 && v !== "").map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`);
263
+ return parts.length ? `?${parts.join("&")}` : "";
264
+ }
265
+ function getAuthorizationValue(apiKey) {
266
+ return apiKey.trim();
267
+ }
268
+ async function apiRequest(config, options) {
269
+ const apiKey = resolveApiKey(config);
270
+ if (!apiKey) {
271
+ throw new CliError(
272
+ "\u672A\u914D\u7F6E API Key",
273
+ ExitCode.CONFIG_MISSING,
274
+ "\u8FD0\u884C maptec init \u6216\u8BBE\u7F6E MAPTEC_API_KEY \u73AF\u5883\u53D8\u91CF"
275
+ );
276
+ }
277
+ const baseUrl = resolveBaseUrl(config);
278
+ if (!baseUrl) {
279
+ throw new CliError(
280
+ "\u672A\u914D\u7F6E Base URL",
281
+ ExitCode.CONFIG_MISSING,
282
+ "\u8FD0\u884C maptec init \u6216\u8BBE\u7F6E MAPTEC_BASE_URL \u73AF\u5883\u53D8\u91CF"
283
+ );
284
+ }
285
+ const timeoutMs = options.timeoutMs ?? config.timeoutMs;
286
+ const queryString = options.query ? buildQueryString(options.query) : "";
287
+ const url = `${baseUrl}${options.path}${queryString}`;
288
+ const headers = {
289
+ Authorization: getAuthorizationValue(apiKey)
290
+ };
291
+ const method = options.method ?? "GET";
292
+ const init = {
293
+ method,
294
+ headers,
295
+ signal: AbortSignal.timeout(timeoutMs)
296
+ };
297
+ if (options.body !== void 0) {
298
+ headers["Content-Type"] = "application/json";
299
+ init.body = JSON.stringify(options.body);
300
+ }
301
+ let response;
302
+ try {
303
+ response = await fetch(url, init);
304
+ } catch (err) {
305
+ const message = err instanceof Error && err.name === "TimeoutError" ? `\u8BF7\u6C42\u8D85\u65F6\uFF08${timeoutMs}ms\uFF09` : err instanceof Error ? err.message : "\u7F51\u7EDC\u8BF7\u6C42\u5931\u8D25";
306
+ throw new CliError(message, ExitCode.NETWORK_ERROR);
307
+ }
308
+ let data;
309
+ try {
310
+ data = await response.json();
311
+ } catch {
312
+ throw new CliError(
313
+ `\u65E0\u6CD5\u89E3\u6790 API \u54CD\u5E94\uFF08HTTP ${response.status}\uFF09`,
314
+ response.ok ? ExitCode.INTERNAL_ERROR : ExitCode.API_ERROR
315
+ );
316
+ }
317
+ if (!response.ok) {
318
+ const code = data.error?.code ?? data.code ?? `HTTP_${response.status}`;
319
+ const message = data.error?.message ?? data.message ?? `HTTP ${response.status}`;
320
+ throw new CliError(message, ExitCode.API_ERROR, code);
321
+ }
322
+ return data;
323
+ }
324
+ function searchPath(segment) {
325
+ return `/search/${API_VERSION}/${segment}`;
326
+ }
327
+ function routesPath(segment) {
328
+ return `/routes/${API_VERSION}/${segment}`;
329
+ }
330
+
331
+ // src/api/geocode.ts
332
+ async function forwardGeocode(config, params) {
333
+ const query = {
334
+ address: params.address,
335
+ language: params.language,
336
+ region: params.region,
337
+ components: params.components,
338
+ locationBias: params.locationBias
339
+ };
340
+ const data = await apiRequest(config, {
341
+ path: searchPath("geocode"),
342
+ query
343
+ });
344
+ if (!Array.isArray(data.results)) {
345
+ data.results = [];
346
+ }
347
+ if (data.status === "ERROR" && data.error) {
348
+ return data;
349
+ }
350
+ return data;
351
+ }
352
+ async function reverseGeocode(config, params) {
353
+ const query = {
354
+ location: `${params.lat},${params.lng}`,
355
+ language: params.language,
356
+ region: params.region,
357
+ resultType: params.resultType,
358
+ locationType: params.locationType
359
+ };
360
+ const data = await apiRequest(config, {
361
+ path: searchPath("reverse"),
362
+ query
363
+ });
364
+ if (!Array.isArray(data.results)) {
365
+ data.results = [];
366
+ }
367
+ return data;
368
+ }
369
+
370
+ // src/commands/geocode.ts
371
+ async function runGeocode(opts, outputOpts) {
372
+ if (!opts.address?.trim()) {
373
+ throw new CliError(
374
+ "\u7F3A\u5C11 --address \u53C2\u6570",
375
+ ExitCode.USER_ERROR,
376
+ '\u793A\u4F8B\uFF1Amaptec geocode --address "\u5317\u4EAC\u5E02\u671D\u9633\u533A\u56FD\u8D38"'
377
+ );
378
+ }
379
+ const config = await loadConfig();
380
+ const result = await forwardGeocode(config, {
381
+ address: opts.address.trim(),
382
+ language: opts.language,
383
+ region: opts.region
384
+ });
385
+ if (result.status === "ZERO_RESULTS") {
386
+ throw new CliError(
387
+ "ZERO_RESULTS \u2014 \u672A\u627E\u5230\u5339\u914D\u5730\u5740",
388
+ ExitCode.API_ERROR,
389
+ "\u68C0\u67E5 --address \u6216\u6539\u7528 maptec search text"
390
+ );
391
+ }
392
+ handleApiBusinessStatus(result, "geocode");
393
+ await emitSuccess(
394
+ { command: "geocode", data: result },
395
+ outputOpts
396
+ );
397
+ }
398
+
399
+ // src/commands/reverse-geocode.ts
400
+ async function runReverseGeocode(opts, outputOpts) {
401
+ if (!Number.isFinite(opts.lat) || !Number.isFinite(opts.lng)) {
402
+ throw new CliError(
403
+ "\u7F3A\u5C11\u6709\u6548\u7684 --lat \u4E0E --lng",
404
+ ExitCode.USER_ERROR,
405
+ "\u793A\u4F8B\uFF1Amaptec reverse-geocode --lat 39.908 --lng 116.397"
406
+ );
407
+ }
408
+ const config = await loadConfig();
409
+ const result = await reverseGeocode(config, opts);
410
+ if (result.status === "ZERO_RESULTS") {
411
+ throw new CliError(
412
+ "ZERO_RESULTS \u2014 \u8BE5\u5750\u6807\u65E0\u5730\u5740\u7ED3\u679C",
413
+ ExitCode.API_ERROR,
414
+ "\u68C0\u67E5\u5750\u6807\u662F\u5426\u5728\u670D\u52A1\u8303\u56F4\u5185"
415
+ );
416
+ }
417
+ handleApiBusinessStatus(result, "reverse-geocode");
418
+ await emitSuccess(
419
+ { command: "reverse-geocode", data: result },
420
+ outputOpts
421
+ );
422
+ }
423
+
424
+ // src/api/location.ts
425
+ function parseLngLatPair(value, defaultRadius) {
426
+ const parts = value.split(",").map((s) => s.trim());
427
+ if (parts.length < 2) {
428
+ throw new Error(`\u65E0\u6548\u5750\u6807 "${value}"\uFF0C\u683C\u5F0F\uFF1Alng,lat \u6216 lng,lat,radius`);
429
+ }
430
+ const lng = Number(parts[0]);
431
+ const lat = Number(parts[1]);
432
+ if (!Number.isFinite(lng) || !Number.isFinite(lat)) {
433
+ throw new Error(`\u65E0\u6548\u5750\u6807 "${value}"`);
434
+ }
435
+ const radius = parts[2] !== void 0 ? Number(parts[2]) : defaultRadius;
436
+ return {
437
+ center: { longitude: lng, latitude: lat },
438
+ radius: radius !== void 0 && Number.isFinite(radius) ? radius : void 0
439
+ };
440
+ }
441
+ function toLocationAreaCircle(lng, lat, radius) {
442
+ return {
443
+ center: { longitude: lng, latitude: lat },
444
+ radius
445
+ };
446
+ }
447
+ function buildRouteEndpoint(point) {
448
+ if (point.kind === "coord") {
449
+ return {
450
+ location: {
451
+ latitude: point.lat,
452
+ longitude: point.lng
453
+ }
454
+ };
455
+ }
456
+ const text = point.value.trim();
457
+ if (/^[\d.+-]+,[\d.+-]+$/.test(text)) {
458
+ const { center } = parseLngLatPair(text);
459
+ return { location: center };
460
+ }
461
+ return { placeId: text };
462
+ }
463
+
464
+ // src/api/place-search.ts
465
+ async function searchText(config, params) {
466
+ const body = {
467
+ query: params.query,
468
+ pageSize: params.pageSize ?? 20,
469
+ language: params.language ?? "en"
470
+ };
471
+ if (params.region) body.region = params.region;
472
+ if (params.type) body.type = params.type;
473
+ if (params.rank) body.rank = params.rank;
474
+ if (params.location) {
475
+ const { center, radius } = parseLngLatPair(params.location, 5e3);
476
+ body.locationBias = toLocationAreaCircle(
477
+ center.longitude,
478
+ center.latitude,
479
+ radius ?? 5e3
480
+ );
481
+ }
482
+ return apiRequest(config, {
483
+ method: "POST",
484
+ path: searchPath("text"),
485
+ body
486
+ });
487
+ }
488
+ async function searchNearby(config, params) {
489
+ const body = {
490
+ locationLimit: toLocationAreaCircle(
491
+ params.lng,
492
+ params.lat,
493
+ params.radius ?? 500
494
+ ),
495
+ types: params.types ?? "",
496
+ resultLimit: params.resultLimit ?? 20,
497
+ rank: params.rank ?? "DISTANCE",
498
+ language: params.language ?? "en"
499
+ };
500
+ if (params.region) body.region = params.region;
501
+ return apiRequest(config, {
502
+ method: "POST",
503
+ path: searchPath("nearby"),
504
+ body
505
+ });
506
+ }
507
+ async function searchSuggest(config, params) {
508
+ const body = {
509
+ query: params.query,
510
+ resultLimit: params.resultLimit ?? 20,
511
+ language: params.language ?? "en",
512
+ region: params.region ?? "",
513
+ types: ""
514
+ };
515
+ if (params.location) {
516
+ const { center, radius } = parseLngLatPair(params.location, 5e3);
517
+ body.locationBias = toLocationAreaCircle(
518
+ center.longitude,
519
+ center.latitude,
520
+ radius ?? 5e3
521
+ );
522
+ }
523
+ return apiRequest(config, {
524
+ method: "POST",
525
+ path: searchPath("suggest"),
526
+ body
527
+ });
528
+ }
529
+ async function getPlaceDetails(config, params) {
530
+ const query = {
531
+ language: params.language ?? "en",
532
+ region: params.region,
533
+ sessionToken: params.sessionToken
534
+ };
535
+ return apiRequest(config, {
536
+ path: searchPath(`places/${encodeURIComponent(params.placeId)}`),
537
+ query
538
+ });
539
+ }
540
+
541
+ // src/commands/search.ts
542
+ function assertOkStatus(result, command) {
543
+ if (result.status === "ZERO_RESULTS") {
544
+ throw new CliError(
545
+ "ZERO_RESULTS \u2014 \u672A\u627E\u5230\u5339\u914D\u7ED3\u679C",
546
+ ExitCode.API_ERROR,
547
+ `\u68C0\u67E5\u641C\u7D22\u53C2\u6570\uFF0C\u547D\u4EE4\uFF1A${command}`
548
+ );
549
+ }
550
+ handleApiBusinessStatus(result, command);
551
+ }
552
+ async function runSearchText(opts, outputOpts) {
553
+ if (!opts.query?.trim()) {
554
+ throw new CliError("\u7F3A\u5C11 --query", ExitCode.USER_ERROR);
555
+ }
556
+ const config = await loadConfig();
557
+ const result = await searchText(config, opts);
558
+ assertOkStatus(result, "search.text");
559
+ await emitSuccess({ command: "search.text", data: result }, outputOpts);
560
+ }
561
+ async function runSearchNearby(opts, outputOpts) {
562
+ const config = await loadConfig();
563
+ const result = await searchNearby(config, {
564
+ lat: opts.lat,
565
+ lng: opts.lng,
566
+ radius: opts.radius,
567
+ types: opts.types,
568
+ resultLimit: opts.limit,
569
+ language: opts.language,
570
+ region: opts.region,
571
+ rank: opts.rank
572
+ });
573
+ assertOkStatus(result, "search.nearby");
574
+ await emitSuccess({ command: "search.nearby", data: result }, outputOpts);
575
+ }
576
+ async function runSearchSuggest(opts, outputOpts) {
577
+ const config = await loadConfig();
578
+ const result = await searchSuggest(config, {
579
+ query: opts.query,
580
+ location: opts.location,
581
+ language: opts.language,
582
+ region: opts.region,
583
+ resultLimit: opts.limit
584
+ });
585
+ assertOkStatus(result, "search.suggest");
586
+ await emitSuccess({ command: "search.suggest", data: result }, outputOpts);
587
+ }
588
+ async function runSearchPlace(opts, outputOpts) {
589
+ const config = await loadConfig();
590
+ const result = await getPlaceDetails(config, {
591
+ placeId: opts.id,
592
+ language: opts.language,
593
+ region: opts.region,
594
+ sessionToken: opts.sessionToken
595
+ });
596
+ assertOkStatus(result, "search.place");
597
+ await emitSuccess({ command: "search.place", data: result }, outputOpts);
598
+ }
599
+
600
+ // src/api/directions.ts
601
+ async function getDirectionsWithEndpoints(config, params) {
602
+ const body = {
603
+ origin: params.origin,
604
+ destination: params.destination,
605
+ mode: params.mode
606
+ };
607
+ if (params.strategy) body.strategy = params.strategy;
608
+ if (params.language) body.language = params.language;
609
+ if (params.vehicleSpec) body.vehicleSpec = params.vehicleSpec;
610
+ return apiRequest(config, {
611
+ method: "POST",
612
+ path: routesPath("directions"),
613
+ body
614
+ });
615
+ }
616
+
617
+ // src/api/route-resolve.ts
618
+ async function resolveRouteEndpoint(config, point) {
619
+ if (point.kind === "coord") {
620
+ return buildRouteEndpoint(point);
621
+ }
622
+ const text = point.value.trim();
623
+ if (/^[\d.+-]+,[\d.+-]+$/.test(text)) {
624
+ return buildRouteEndpoint(point);
625
+ }
626
+ if (text.startsWith("places/") || text.includes("_v0.") || /^[\d]+$/.test(text)) {
627
+ return buildRouteEndpoint(point);
628
+ }
629
+ const geocoded = await forwardGeocode(config, { address: text });
630
+ const first = geocoded.results?.[0];
631
+ if (geocoded.status !== "OK" || !first?.geometry?.location) {
632
+ throw new Error(
633
+ `\u65E0\u6CD5 geocode \u5730\u5740 "${text}"\uFF08${geocoded.status}\uFF09`
634
+ );
635
+ }
636
+ const { latitude, longitude } = first.geometry.location;
637
+ return buildRouteEndpoint({ kind: "coord", lat: latitude, lng: longitude });
638
+ }
639
+
640
+ // src/output/route-fields.ts
641
+ var DEFAULT_ROUTE_FIELDS = ["distance", "duration", "summary"];
642
+ function shapeRouteOutput(data, fields) {
643
+ if (!data.routes?.length) return data;
644
+ const wanted = fields?.length ? fields : DEFAULT_ROUTE_FIELDS;
645
+ const includePolyline = wanted.includes("polyline");
646
+ const routes = data.routes.map(
647
+ (route) => projectRoute(route, wanted, includePolyline)
648
+ );
649
+ return { ...data, routes };
650
+ }
651
+ function projectRoute(route, fields, includePolyline) {
652
+ const out = {};
653
+ if (fields.includes("summary") || fields.includes("distance") || fields.includes("duration")) {
654
+ const summary = {};
655
+ if (fields.includes("distance")) {
656
+ summary.distanceMeters = route.summary.distanceMeters;
657
+ }
658
+ if (fields.includes("duration")) {
659
+ summary.durationSeconds = route.summary.durationSeconds;
660
+ }
661
+ if (fields.includes("summary")) {
662
+ summary.trafficDurationSeconds = route.summary.trafficDurationSeconds;
663
+ }
664
+ out.summary = summary;
665
+ }
666
+ if (fields.includes("warnings") && route.warnings) {
667
+ out.warnings = route.warnings;
668
+ }
669
+ if (includePolyline) {
670
+ const polyline = route.overviewPolyline;
671
+ if (polyline) out.polyline = polyline;
672
+ if (route.path) out.path = route.path;
673
+ }
674
+ if (fields.includes("legs") && route.legs) {
675
+ out.legs = route.legs;
676
+ }
677
+ return out;
678
+ }
679
+
680
+ // src/commands/route.ts
681
+ function resolveEndpoint(text, lat, lng, label) {
682
+ if (lat !== void 0 && lng !== void 0 && Number.isFinite(lat) && Number.isFinite(lng)) {
683
+ return { kind: "coord", lat, lng };
684
+ }
685
+ if (text?.trim()) {
686
+ return { kind: "text", value: text.trim() };
687
+ }
688
+ throw new CliError(
689
+ `\u7F3A\u5C11\u8D77\u70B9/\u7EC8\u70B9 ${label}`,
690
+ ExitCode.USER_ERROR,
691
+ `\u4F7F\u7528 --${label} \u6216 --${label}-lat + --${label}-lng`
692
+ );
693
+ }
694
+ async function runRoute(opts, outputOpts) {
695
+ const origin = resolveEndpoint(opts.from, opts.fromLat, opts.fromLng, "from");
696
+ const destination = resolveEndpoint(opts.to, opts.toLat, opts.toLng, "to");
697
+ const config = await loadConfig();
698
+ const [originParam, destinationParam] = await Promise.all([
699
+ resolveRouteEndpoint(config, origin),
700
+ resolveRouteEndpoint(config, destination)
701
+ ]);
702
+ const result = await getDirectionsWithEndpoints(config, {
703
+ mode: opts.mode,
704
+ origin: originParam,
705
+ destination: destinationParam,
706
+ strategy: opts.strategy,
707
+ language: opts.language,
708
+ vehicleSpec: opts.mode === "truck" ? {
709
+ heightMeters: opts.height,
710
+ weightTons: opts.weight,
711
+ lengthMeters: opts.length
712
+ } : void 0
713
+ });
714
+ if (result.status === "NO_ROUTE_FOUND") {
715
+ throw new CliError(
716
+ "NO_ROUTE_FOUND \u2014 \u65E0\u6CD5\u89C4\u5212\u8DEF\u7EBF",
717
+ ExitCode.API_ERROR,
718
+ "\u68C0\u67E5\u8D77\u7EC8\u70B9\u6216\u6539\u7528 maptec geocode \u786E\u8BA4\u5750\u6807"
719
+ );
720
+ }
721
+ handleApiBusinessStatus(result, `route.${opts.mode}`);
722
+ const fields = outputOpts.fields ?? opts.fields;
723
+ const shaped = shapeRouteOutput(result, fields);
724
+ await emitSuccess(
725
+ { command: `route.${opts.mode}`, data: shaped },
726
+ { ...outputOpts, fields: void 0 }
727
+ );
728
+ }
729
+
730
+ // src/cli.ts
731
+ function getVersion() {
732
+ try {
733
+ const dir = dirname(fileURLToPath(import.meta.url));
734
+ const pkgPath = join(dir, "..", "package.json");
735
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
736
+ return pkg.version;
737
+ } catch {
738
+ return "0.0.0";
739
+ }
740
+ }
741
+ function parseFields(value) {
742
+ return value.split(",").map((s) => s.trim()).filter(Boolean);
743
+ }
744
+ function buildOutputOptions(globalOpts) {
745
+ const format = globalOpts.format ?? "json";
746
+ if (format !== "json" && format !== "table") {
747
+ throw new CliError(`\u65E0\u6548 --format "${globalOpts.format}"`, ExitCode.USER_ERROR);
748
+ }
749
+ return {
750
+ format,
751
+ out: globalOpts.out,
752
+ quiet: globalOpts.quiet,
753
+ fields: globalOpts.fields ? parseFields(globalOpts.fields) : void 0
754
+ };
755
+ }
756
+ async function main() {
757
+ const program = new Command();
758
+ program.name("maptec").description("Maptec \u5730\u56FE CLI \u2014 \u7ED3\u6784\u5316 JSON \u8F93\u51FA\uFF0C\u4F9B\u5F00\u53D1\u8005\u4E0E AI Agent \u8C03\u7528").version(getVersion(), "-V, --version", "\u663E\u793A\u7248\u672C\u53F7");
759
+ program.option("--format <json|table>", "\u8F93\u51FA\u683C\u5F0F", "json").option("--out <file>", "\u5199\u5165\u6587\u4EF6\u800C\u975E stdout").option("--quiet", "\u51CF\u5C11 stderr \u8FDB\u5EA6\u4FE1\u606F").option("--timeout <ms>", "\u8986\u76D6\u9ED8\u8BA4\u8BF7\u6C42\u8D85\u65F6\uFF08\u6BEB\u79D2\uFF09").option("--fields <list>", "\u88C1\u526A\u8F93\u51FA\u5B57\u6BB5\uFF0C\u9017\u53F7\u5206\u9694");
760
+ program.command("init").description("\u4EA4\u4E92\u5F0F\u914D\u7F6E apiKey \u4E0E baseUrl").action(async () => {
761
+ const globalOpts = program.opts();
762
+ if (globalOpts.timeout) {
763
+ const config = await loadConfig();
764
+ config.timeoutMs = Number(globalOpts.timeout);
765
+ }
766
+ await runInit(buildOutputOptions(globalOpts));
767
+ });
768
+ const configCmd = program.command("config").description("\u8BFB\u5199 ~/.maptec/config.json");
769
+ configCmd.command("get").description("\u6253\u5370\u5F53\u524D\u914D\u7F6E\uFF08apiKey \u8131\u654F\uFF09").action(async () => {
770
+ const globalOpts = program.opts();
771
+ await runConfigGet(buildOutputOptions(globalOpts));
772
+ });
773
+ configCmd.command("set <key> <value>").description("\u8BBE\u7F6E\u914D\u7F6E\u9879\uFF1Aapi-key | base-url | default-format | timeout").action(async (key, value) => {
774
+ const globalOpts = program.opts();
775
+ await runConfigSet(key, value, buildOutputOptions(globalOpts));
776
+ });
777
+ program.command("geocode").description("\u6B63\u5411\u5730\u7406\u7F16\u7801\uFF1A\u5730\u5740 \u2192 \u5750\u6807").requiredOption("--address <text>", "\u5730\u5740\u5B57\u7B26\u4E32").option("--language <code>", "\u8FD4\u56DE\u8BED\u8A00\uFF0C\u5982 zh-CN").option("--region <code>", "\u5730\u533A\u504F\u5411\uFF0C\u5982 CN").action(async (opts) => {
778
+ const globalOpts = program.opts();
779
+ await applyTimeoutOverride(globalOpts.timeout);
780
+ await runGeocode(opts, buildOutputOptions(globalOpts));
781
+ });
782
+ program.command("reverse-geocode").description("\u53CD\u5411\u5730\u7406\u7F16\u7801\uFF1A\u5750\u6807 \u2192 \u5730\u5740").requiredOption("--lat <number>", "\u7EAC\u5EA6", parseFloat).requiredOption("--lng <number>", "\u7ECF\u5EA6", parseFloat).option("--language <code>", "\u8FD4\u56DE\u8BED\u8A00").option("--region <code>", "\u5730\u533A\u504F\u5411").option("--result-type <types>", "\u7ED3\u679C\u7C7B\u578B\u8FC7\u6EE4\uFF0C\u7AD6\u7EBF\u5206\u9694").option("--location-type <types>", "\u7CBE\u5EA6\u7C7B\u578B\u8FC7\u6EE4").action(async (opts) => {
783
+ const globalOpts = program.opts();
784
+ await applyTimeoutOverride(globalOpts.timeout);
785
+ await runReverseGeocode(
786
+ {
787
+ lat: opts.lat,
788
+ lng: opts.lng,
789
+ language: opts.language,
790
+ region: opts.region,
791
+ resultType: opts.resultType,
792
+ locationType: opts.locationType
793
+ },
794
+ buildOutputOptions(globalOpts)
795
+ );
796
+ });
797
+ const searchCmd = program.command("search").description("\u5730\u70B9\u641C\u7D22");
798
+ searchCmd.command("text").description("\u5173\u952E\u8BCD\u6587\u672C\u641C\u7D22").requiredOption("--query <text>", "\u641C\u7D22\u5173\u952E\u8BCD").option("--location <lng,lat>", "\u4F4D\u7F6E\u504F\u5411\uFF08\u5706\u5FC3\uFF09").option("--language <code>", "\u8BED\u8A00").option("--region <code>", "\u5730\u533A").option("--page-size <n>", "\u6BCF\u9875\u6761\u6570", parseInt).option("--type <category>", "POI \u7C7B\u522B").option("--rank <mode>", "RELEVANCE|DISTANCE|POPULARITY").action(async (opts) => {
799
+ const globalOpts = program.opts();
800
+ await applyTimeoutOverride(globalOpts.timeout);
801
+ await runSearchText(opts, buildOutputOptions(globalOpts));
802
+ });
803
+ searchCmd.command("nearby").description("\u9644\u8FD1\u641C\u7D22").requiredOption("--lat <number>", "\u7EAC\u5EA6", parseFloat).requiredOption("--lng <number>", "\u7ECF\u5EA6", parseFloat).option("--radius <meters>", "\u534A\u5F84\uFF08\u7C73\uFF09", parseInt).option("--types <list>", "\u7C7B\u578B\uFF0C\u9017\u53F7\u5206\u9694").option("--limit <n>", "\u7ED3\u679C\u4E0A\u9650", parseInt).option("--language <code>", "\u8BED\u8A00").option("--region <code>", "\u5730\u533A").option("--rank <mode>", "\u6392\u5E8F").action(async (opts) => {
804
+ const globalOpts = program.opts();
805
+ await applyTimeoutOverride(globalOpts.timeout);
806
+ await runSearchNearby(
807
+ {
808
+ lat: opts.lat,
809
+ lng: opts.lng,
810
+ radius: opts.radius,
811
+ types: opts.types,
812
+ limit: opts.limit,
813
+ language: opts.language,
814
+ region: opts.region,
815
+ rank: opts.rank
816
+ },
817
+ buildOutputOptions(globalOpts)
818
+ );
819
+ });
820
+ searchCmd.command("suggest").description("\u8F93\u5165\u63D0\u793A / \u81EA\u52A8\u8865\u5168").requiredOption("--query <text>", "\u8F93\u5165\u5B57\u7B26\u4E32").option("--location <lng,lat>", "\u4F4D\u7F6E\u504F\u5411").option("--language <code>", "\u8BED\u8A00").option("--region <code>", "\u5730\u533A").option("--limit <n>", "\u7ED3\u679C\u4E0A\u9650", parseInt).action(async (opts) => {
821
+ const globalOpts = program.opts();
822
+ await applyTimeoutOverride(globalOpts.timeout);
823
+ await runSearchSuggest(opts, buildOutputOptions(globalOpts));
824
+ });
825
+ searchCmd.command("place").description("\u5730\u70B9\u8BE6\u60C5").requiredOption("--id <placeId>", "\u5730\u70B9 ID").option("--language <code>", "\u8BED\u8A00").option("--region <code>", "\u5730\u533A").option("--session-token <token>", "\u4F1A\u8BDD\u4EE4\u724C").action(async (opts) => {
826
+ const globalOpts = program.opts();
827
+ await applyTimeoutOverride(globalOpts.timeout);
828
+ await runSearchPlace(
829
+ {
830
+ id: opts.id,
831
+ language: opts.language,
832
+ region: opts.region,
833
+ sessionToken: opts.sessionToken
834
+ },
835
+ buildOutputOptions(globalOpts)
836
+ );
837
+ });
838
+ const routeCmd = program.command("route").description("\u8DEF\u7EBF\u89C4\u5212");
839
+ const addRouteOptions = (cmd) => {
840
+ cmd.option("--from <text>", "\u8D77\u70B9\u5730\u5740\u6216 lng,lat").option("--to <text>", "\u7EC8\u70B9\u5730\u5740\u6216 lng,lat").option("--from-lat <number>", "\u8D77\u70B9\u7EAC\u5EA6", parseFloat).option("--from-lng <number>", "\u8D77\u70B9\u7ECF\u5EA6", parseFloat).option("--to-lat <number>", "\u7EC8\u70B9\u7EAC\u5EA6", parseFloat).option("--to-lng <number>", "\u7EC8\u70B9\u7ECF\u5EA6", parseFloat).option("--strategy <name>", "fastest|shortest|eco|balanced").option("--language <code>", "\u5BFC\u822A\u8BED\u8A00");
841
+ };
842
+ addRouteOptions(
843
+ routeCmd.command("driving").description("\u9A7E\u8F66\u8DEF\u7EBF").action(async (opts) => {
844
+ const globalOpts = program.opts();
845
+ await applyTimeoutOverride(globalOpts.timeout);
846
+ await runRoute(
847
+ {
848
+ mode: "driving",
849
+ from: opts.from,
850
+ to: opts.to,
851
+ fromLat: opts.fromLat,
852
+ fromLng: opts.fromLng,
853
+ toLat: opts.toLat,
854
+ toLng: opts.toLng,
855
+ strategy: opts.strategy,
856
+ language: opts.language
857
+ },
858
+ buildOutputOptions(globalOpts)
859
+ );
860
+ })
861
+ );
862
+ addRouteOptions(
863
+ routeCmd.command("truck").description("\u8D27\u8F66\u8DEF\u7EBF").option("--height <meters>", "\u8F66\u9AD8\uFF08\u7C73\uFF09", parseFloat).option("--weight <tons>", "\u603B\u91CD\uFF08\u5428\uFF09", parseFloat).option("--length <meters>", "\u8F66\u957F\uFF08\u7C73\uFF09", parseFloat).action(async (opts) => {
864
+ const globalOpts = program.opts();
865
+ await applyTimeoutOverride(globalOpts.timeout);
866
+ await runRoute(
867
+ {
868
+ mode: "truck",
869
+ from: opts.from,
870
+ to: opts.to,
871
+ fromLat: opts.fromLat,
872
+ fromLng: opts.fromLng,
873
+ toLat: opts.toLat,
874
+ toLng: opts.toLng,
875
+ strategy: opts.strategy,
876
+ language: opts.language,
877
+ height: opts.height,
878
+ weight: opts.weight,
879
+ length: opts.length
880
+ },
881
+ buildOutputOptions(globalOpts)
882
+ );
883
+ })
884
+ );
885
+ await program.parseAsync(process.argv);
886
+ }
887
+ async function applyTimeoutOverride(timeout) {
888
+ if (!timeout) return;
889
+ const ms = Number(timeout);
890
+ if (!Number.isFinite(ms) || ms <= 0) {
891
+ throw new CliError("\u65E0\u6548 --timeout \u503C", ExitCode.USER_ERROR);
892
+ }
893
+ const config = await loadConfig();
894
+ const { saveConfig: saveConfig2 } = await import("./store-ZU7HLXCV.js");
895
+ config.timeoutMs = ms;
896
+ await saveConfig2(config);
897
+ }
898
+ main().catch(exitWithError);
899
+ //# sourceMappingURL=cli.js.map