@geolonia/geonicdb-cli 0.1.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 +459 -0
- package/dist/index.js +3413 -0
- package/dist/index.js.map +1 -0
- package/package.json +73 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,3413 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { createRequire as createRequire3 } from "module";
|
|
5
|
+
import { Command as Command2 } from "commander";
|
|
6
|
+
|
|
7
|
+
// src/config.ts
|
|
8
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { homedir } from "os";
|
|
11
|
+
function getConfigDir() {
|
|
12
|
+
return process.env.GEONIC_CONFIG_DIR ?? join(homedir(), ".config", "geonic");
|
|
13
|
+
}
|
|
14
|
+
function getConfigFile() {
|
|
15
|
+
return join(getConfigDir(), "config.json");
|
|
16
|
+
}
|
|
17
|
+
function ensureConfigDir() {
|
|
18
|
+
const dir = getConfigDir();
|
|
19
|
+
if (!existsSync(dir)) {
|
|
20
|
+
mkdirSync(dir, { recursive: true });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function migrateV1ToV2(data) {
|
|
24
|
+
const profile = {};
|
|
25
|
+
const knownKeys = [
|
|
26
|
+
"url",
|
|
27
|
+
"service",
|
|
28
|
+
"token",
|
|
29
|
+
"refreshToken",
|
|
30
|
+
"format",
|
|
31
|
+
"apiKey"
|
|
32
|
+
];
|
|
33
|
+
for (const key of knownKeys) {
|
|
34
|
+
if (key in data) {
|
|
35
|
+
profile[key] = data[key];
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
version: 2,
|
|
40
|
+
currentProfile: "default",
|
|
41
|
+
profiles: { default: profile }
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function isGdbConfigFile(value) {
|
|
45
|
+
if (typeof value !== "object" || value === null) return false;
|
|
46
|
+
const v = value;
|
|
47
|
+
return v.version === 2 && typeof v.currentProfile === "string" && typeof v.profiles === "object" && v.profiles !== null;
|
|
48
|
+
}
|
|
49
|
+
function defaultConfig() {
|
|
50
|
+
return { version: 2, currentProfile: "default", profiles: { default: {} } };
|
|
51
|
+
}
|
|
52
|
+
function loadConfigFile() {
|
|
53
|
+
try {
|
|
54
|
+
const raw = readFileSync(getConfigFile(), "utf-8");
|
|
55
|
+
const data = JSON.parse(raw);
|
|
56
|
+
if (!("version" in data)) {
|
|
57
|
+
const migrated = migrateV1ToV2(data);
|
|
58
|
+
saveConfigFile(migrated);
|
|
59
|
+
return migrated;
|
|
60
|
+
}
|
|
61
|
+
if (!isGdbConfigFile(data)) {
|
|
62
|
+
return defaultConfig();
|
|
63
|
+
}
|
|
64
|
+
return data;
|
|
65
|
+
} catch {
|
|
66
|
+
return defaultConfig();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function saveConfigFile(configFile) {
|
|
70
|
+
ensureConfigDir();
|
|
71
|
+
writeFileSync(getConfigFile(), JSON.stringify(configFile, null, 2) + "\n", "utf-8");
|
|
72
|
+
}
|
|
73
|
+
function loadConfig(profileName) {
|
|
74
|
+
const configFile = loadConfigFile();
|
|
75
|
+
const name = profileName ?? configFile.currentProfile;
|
|
76
|
+
return configFile.profiles[name] ?? {};
|
|
77
|
+
}
|
|
78
|
+
function saveConfig(config, profileName) {
|
|
79
|
+
const configFile = loadConfigFile();
|
|
80
|
+
const name = profileName ?? configFile.currentProfile;
|
|
81
|
+
configFile.profiles[name] = config;
|
|
82
|
+
saveConfigFile(configFile);
|
|
83
|
+
}
|
|
84
|
+
function getConfigValue(key, profileName) {
|
|
85
|
+
const config = loadConfig(profileName);
|
|
86
|
+
return config[key];
|
|
87
|
+
}
|
|
88
|
+
function validateUrl(url) {
|
|
89
|
+
url = url.trim();
|
|
90
|
+
if (!url) {
|
|
91
|
+
throw new Error("URL must not be empty.");
|
|
92
|
+
}
|
|
93
|
+
if (!/^https?:\/\//i.test(url)) {
|
|
94
|
+
throw new Error(`Invalid URL: "${url}". URL must start with http:// or https://.`);
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
new URL(url);
|
|
98
|
+
} catch {
|
|
99
|
+
throw new Error(`Invalid URL: "${url}".`);
|
|
100
|
+
}
|
|
101
|
+
return url.replace(/\/+$/, "") + "/";
|
|
102
|
+
}
|
|
103
|
+
function setConfigValue(key, value, profileName) {
|
|
104
|
+
const config = loadConfig(profileName);
|
|
105
|
+
if (key === "url") {
|
|
106
|
+
value = validateUrl(value);
|
|
107
|
+
}
|
|
108
|
+
config[key] = value;
|
|
109
|
+
saveConfig(config, profileName);
|
|
110
|
+
}
|
|
111
|
+
function deleteConfigValue(key, profileName) {
|
|
112
|
+
const config = loadConfig(profileName);
|
|
113
|
+
delete config[key];
|
|
114
|
+
saveConfig(config, profileName);
|
|
115
|
+
}
|
|
116
|
+
function getConfigPath() {
|
|
117
|
+
return getConfigFile();
|
|
118
|
+
}
|
|
119
|
+
function listProfiles() {
|
|
120
|
+
const configFile = loadConfigFile();
|
|
121
|
+
return Object.keys(configFile.profiles).map((name) => ({
|
|
122
|
+
name,
|
|
123
|
+
active: name === configFile.currentProfile
|
|
124
|
+
}));
|
|
125
|
+
}
|
|
126
|
+
function getCurrentProfile() {
|
|
127
|
+
return loadConfigFile().currentProfile;
|
|
128
|
+
}
|
|
129
|
+
function setCurrentProfile(name) {
|
|
130
|
+
const configFile = loadConfigFile();
|
|
131
|
+
if (!(name in configFile.profiles)) {
|
|
132
|
+
throw new Error(`Profile "${name}" does not exist.`);
|
|
133
|
+
}
|
|
134
|
+
configFile.currentProfile = name;
|
|
135
|
+
saveConfigFile(configFile);
|
|
136
|
+
}
|
|
137
|
+
function createProfile(name) {
|
|
138
|
+
const configFile = loadConfigFile();
|
|
139
|
+
if (name in configFile.profiles) {
|
|
140
|
+
throw new Error(`Profile "${name}" already exists.`);
|
|
141
|
+
}
|
|
142
|
+
configFile.profiles[name] = {};
|
|
143
|
+
saveConfigFile(configFile);
|
|
144
|
+
}
|
|
145
|
+
function deleteProfile(name) {
|
|
146
|
+
if (name === "default") {
|
|
147
|
+
throw new Error('Cannot delete the "default" profile.');
|
|
148
|
+
}
|
|
149
|
+
const configFile = loadConfigFile();
|
|
150
|
+
if (!(name in configFile.profiles)) {
|
|
151
|
+
throw new Error(`Profile "${name}" does not exist.`);
|
|
152
|
+
}
|
|
153
|
+
delete configFile.profiles[name];
|
|
154
|
+
if (configFile.currentProfile === name) {
|
|
155
|
+
configFile.currentProfile = "default";
|
|
156
|
+
}
|
|
157
|
+
saveConfigFile(configFile);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/output.ts
|
|
161
|
+
import chalk from "chalk";
|
|
162
|
+
function formatOutput(data, format) {
|
|
163
|
+
switch (format) {
|
|
164
|
+
case "json":
|
|
165
|
+
return JSON.stringify(data, null, 2);
|
|
166
|
+
case "table":
|
|
167
|
+
return formatTable(data);
|
|
168
|
+
case "geojson":
|
|
169
|
+
return JSON.stringify(toGeoJSON(data), null, 2);
|
|
170
|
+
default:
|
|
171
|
+
return JSON.stringify(data, null, 2);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function printOutput(data, format) {
|
|
175
|
+
console.log(formatOutput(data, format));
|
|
176
|
+
}
|
|
177
|
+
function printSuccess(message) {
|
|
178
|
+
console.log(chalk.green(message));
|
|
179
|
+
}
|
|
180
|
+
function printError(message) {
|
|
181
|
+
console.error(chalk.red(`Error: ${message}`));
|
|
182
|
+
}
|
|
183
|
+
function printInfo(message) {
|
|
184
|
+
console.log(chalk.cyan(message));
|
|
185
|
+
}
|
|
186
|
+
function printWarning(message) {
|
|
187
|
+
console.error(chalk.yellow(message));
|
|
188
|
+
}
|
|
189
|
+
function printCount(count) {
|
|
190
|
+
console.log(chalk.dim(`Count: ${count}`));
|
|
191
|
+
}
|
|
192
|
+
function formatTable(data) {
|
|
193
|
+
if (!Array.isArray(data)) {
|
|
194
|
+
if (typeof data === "object" && data !== null) {
|
|
195
|
+
return formatObjectTable(data);
|
|
196
|
+
}
|
|
197
|
+
return String(data);
|
|
198
|
+
}
|
|
199
|
+
if (data.length === 0) return "(empty)";
|
|
200
|
+
const items = data;
|
|
201
|
+
const keys = collectKeys(items);
|
|
202
|
+
const widths = /* @__PURE__ */ new Map();
|
|
203
|
+
for (const key of keys) {
|
|
204
|
+
widths.set(key, key.length);
|
|
205
|
+
}
|
|
206
|
+
for (const item of items) {
|
|
207
|
+
for (const key of keys) {
|
|
208
|
+
const val = cellValue(item[key]);
|
|
209
|
+
widths.set(key, Math.max(widths.get(key), val.length));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
const header2 = keys.map((k) => chalk.bold(k.padEnd(widths.get(k)))).join(" ");
|
|
213
|
+
const separator = keys.map((k) => "\u2500".repeat(widths.get(k))).join("\u2500\u2500");
|
|
214
|
+
const rows = items.map(
|
|
215
|
+
(item) => keys.map((k) => cellValue(item[k]).padEnd(widths.get(k))).join(" ")
|
|
216
|
+
);
|
|
217
|
+
return [header2, separator, ...rows].join("\n");
|
|
218
|
+
}
|
|
219
|
+
function formatObjectTable(obj) {
|
|
220
|
+
const entries = Object.entries(obj);
|
|
221
|
+
if (entries.length === 0) return "(empty)";
|
|
222
|
+
const keyWidth = Math.max(...entries.map(([k]) => k.length));
|
|
223
|
+
return entries.map(([k, v]) => `${chalk.bold(k.padEnd(keyWidth))} ${cellValue(v)}`).join("\n");
|
|
224
|
+
}
|
|
225
|
+
function collectKeys(items) {
|
|
226
|
+
const priority = ["id", "type"];
|
|
227
|
+
const keySet = /* @__PURE__ */ new Set();
|
|
228
|
+
for (const item of items) {
|
|
229
|
+
for (const key of Object.keys(item)) {
|
|
230
|
+
keySet.add(key);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
const sorted = [];
|
|
234
|
+
for (const p of priority) {
|
|
235
|
+
if (keySet.has(p)) {
|
|
236
|
+
sorted.push(p);
|
|
237
|
+
keySet.delete(p);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
sorted.push(...Array.from(keySet).sort());
|
|
241
|
+
return sorted;
|
|
242
|
+
}
|
|
243
|
+
function cellValue(val) {
|
|
244
|
+
if (val === void 0 || val === null) return "";
|
|
245
|
+
if (typeof val === "object") {
|
|
246
|
+
const obj = val;
|
|
247
|
+
if ("value" in obj) return String(obj.value);
|
|
248
|
+
return JSON.stringify(val);
|
|
249
|
+
}
|
|
250
|
+
return String(val);
|
|
251
|
+
}
|
|
252
|
+
function toGeoJSON(data) {
|
|
253
|
+
if (Array.isArray(data)) {
|
|
254
|
+
return {
|
|
255
|
+
type: "FeatureCollection",
|
|
256
|
+
features: data.map(entityToFeature)
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
return entityToFeature(data);
|
|
260
|
+
}
|
|
261
|
+
function entityToFeature(entity) {
|
|
262
|
+
if (typeof entity !== "object" || entity === null) {
|
|
263
|
+
return { type: "Feature", geometry: null, properties: entity };
|
|
264
|
+
}
|
|
265
|
+
const obj = entity;
|
|
266
|
+
let geometry = null;
|
|
267
|
+
if (obj.location) {
|
|
268
|
+
const loc = obj.location;
|
|
269
|
+
geometry = loc.value ?? loc;
|
|
270
|
+
}
|
|
271
|
+
const properties = {};
|
|
272
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
273
|
+
if (key === "location") continue;
|
|
274
|
+
if (typeof value === "object" && value !== null && "value" in value) {
|
|
275
|
+
properties[key] = value.value;
|
|
276
|
+
} else {
|
|
277
|
+
properties[key] = value;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return { type: "Feature", geometry, properties };
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/commands/help.ts
|
|
284
|
+
import chalk2 from "chalk";
|
|
285
|
+
var examplesMap = /* @__PURE__ */ new WeakMap();
|
|
286
|
+
function addExamples(cmd, examples) {
|
|
287
|
+
examplesMap.set(cmd, examples);
|
|
288
|
+
}
|
|
289
|
+
function header(title) {
|
|
290
|
+
return chalk2.yellow.bold(title);
|
|
291
|
+
}
|
|
292
|
+
function findCommand(parent, name) {
|
|
293
|
+
return parent.commands.find(
|
|
294
|
+
(c) => c.name() === name || c.aliases().includes(name)
|
|
295
|
+
);
|
|
296
|
+
}
|
|
297
|
+
function getCommandPath(cmd) {
|
|
298
|
+
const parts = [];
|
|
299
|
+
let current = cmd;
|
|
300
|
+
while (current) {
|
|
301
|
+
parts.unshift(current.name());
|
|
302
|
+
current = current.parent;
|
|
303
|
+
}
|
|
304
|
+
return parts.join(" ");
|
|
305
|
+
}
|
|
306
|
+
function getRootProgram(cmd) {
|
|
307
|
+
let root = cmd;
|
|
308
|
+
while (root.parent) {
|
|
309
|
+
root = root.parent;
|
|
310
|
+
}
|
|
311
|
+
return root;
|
|
312
|
+
}
|
|
313
|
+
function formatOptionSynopsis(opt) {
|
|
314
|
+
const flag = opt.long || opt.short || "";
|
|
315
|
+
if (opt.required) {
|
|
316
|
+
const match = opt.flags.match(/<([^>]+)>/);
|
|
317
|
+
const valueName = match ? match[1] : "value";
|
|
318
|
+
return `[${flag}=<${valueName}>]`;
|
|
319
|
+
} else if (opt.optional) {
|
|
320
|
+
const match = opt.flags.match(/\[([^\]]+)\]/);
|
|
321
|
+
const valueName = match ? match[1] : "value";
|
|
322
|
+
return `[${flag}[=<${valueName}>]]`;
|
|
323
|
+
}
|
|
324
|
+
return `[${flag}]`;
|
|
325
|
+
}
|
|
326
|
+
function formatSynopsis(path, cmd) {
|
|
327
|
+
const parts = [path];
|
|
328
|
+
for (const arg of cmd.registeredArguments) {
|
|
329
|
+
if (arg.required) {
|
|
330
|
+
parts.push(`<${arg.name()}>`);
|
|
331
|
+
} else {
|
|
332
|
+
parts.push(`[<${arg.name()}>]`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
for (const opt of cmd.options) {
|
|
336
|
+
if (opt.hidden) continue;
|
|
337
|
+
parts.push(formatOptionSynopsis(opt));
|
|
338
|
+
}
|
|
339
|
+
return parts.join(" ");
|
|
340
|
+
}
|
|
341
|
+
function formatOptionList(options) {
|
|
342
|
+
const maxLen = Math.max(...options.map((o) => o.flags.length));
|
|
343
|
+
return options.map((opt) => {
|
|
344
|
+
const flags = opt.flags.padEnd(maxLen + 2);
|
|
345
|
+
return ` ${chalk2.green(flags)}${opt.description ?? ""}`;
|
|
346
|
+
});
|
|
347
|
+
}
|
|
348
|
+
function formatGlobalParameters(program2) {
|
|
349
|
+
const lines = [];
|
|
350
|
+
lines.push(header("GLOBAL PARAMETERS"));
|
|
351
|
+
lines.push("");
|
|
352
|
+
lines.push(...formatOptionList(program2.options));
|
|
353
|
+
lines.push("");
|
|
354
|
+
return lines.join("\n");
|
|
355
|
+
}
|
|
356
|
+
function formatTopLevelHelp(program2) {
|
|
357
|
+
const lines = [];
|
|
358
|
+
lines.push(header("NAME"));
|
|
359
|
+
lines.push("");
|
|
360
|
+
lines.push(` ${program2.name()}`);
|
|
361
|
+
lines.push("");
|
|
362
|
+
lines.push(header("DESCRIPTION"));
|
|
363
|
+
lines.push("");
|
|
364
|
+
lines.push(` ${program2.description()}`);
|
|
365
|
+
lines.push("");
|
|
366
|
+
lines.push(header("AVAILABLE COMMANDS"));
|
|
367
|
+
lines.push("");
|
|
368
|
+
const commands = program2.commands.filter((c) => !c._hidden).slice().sort((a, b) => a.name().localeCompare(b.name()));
|
|
369
|
+
const maxLen = Math.max(...commands.map((c) => c.name().length));
|
|
370
|
+
for (const cmd of commands) {
|
|
371
|
+
const name = cmd.name().padEnd(maxLen + 2);
|
|
372
|
+
lines.push(` ${chalk2.green(name)}${cmd.summary() || cmd.description()}`);
|
|
373
|
+
}
|
|
374
|
+
lines.push("");
|
|
375
|
+
lines.push(formatGlobalParameters(program2));
|
|
376
|
+
return lines.join("\n");
|
|
377
|
+
}
|
|
378
|
+
function formatCommandDetails(program2, cmd, path) {
|
|
379
|
+
const lines = [];
|
|
380
|
+
lines.push(header("NAME"));
|
|
381
|
+
lines.push("");
|
|
382
|
+
const aliases = cmd.aliases().filter((a) => a.length > 0);
|
|
383
|
+
if (aliases.length > 0) {
|
|
384
|
+
lines.push(` ${path} ${chalk2.dim(`(alias: ${aliases.join(", ")})`)}`);
|
|
385
|
+
} else {
|
|
386
|
+
lines.push(` ${path}`);
|
|
387
|
+
}
|
|
388
|
+
lines.push("");
|
|
389
|
+
lines.push(header("DESCRIPTION"));
|
|
390
|
+
lines.push("");
|
|
391
|
+
for (const descLine of cmd.description().split("\n")) {
|
|
392
|
+
lines.push(` ${descLine}`);
|
|
393
|
+
}
|
|
394
|
+
const subcommands = cmd.commands.filter(
|
|
395
|
+
(c) => !c._hidden
|
|
396
|
+
);
|
|
397
|
+
if (subcommands.length > 0) {
|
|
398
|
+
lines.push("");
|
|
399
|
+
lines.push(header("SYNOPSIS"));
|
|
400
|
+
lines.push("");
|
|
401
|
+
lines.push(` ${path} <command>`);
|
|
402
|
+
lines.push("");
|
|
403
|
+
lines.push(header("SUBCOMMANDS"));
|
|
404
|
+
lines.push("");
|
|
405
|
+
const maxLen = Math.max(...subcommands.map((c) => c.name().length));
|
|
406
|
+
for (const sub of subcommands) {
|
|
407
|
+
const name = sub.name().padEnd(maxLen + 2);
|
|
408
|
+
lines.push(` ${chalk2.green(name)}${sub.summary() || sub.description()}`);
|
|
409
|
+
}
|
|
410
|
+
} else {
|
|
411
|
+
lines.push("");
|
|
412
|
+
lines.push(header("SYNOPSIS"));
|
|
413
|
+
lines.push("");
|
|
414
|
+
lines.push(` ${formatSynopsis(path, cmd)}`);
|
|
415
|
+
const options = cmd.options.filter((o) => !o.hidden);
|
|
416
|
+
if (options.length > 0) {
|
|
417
|
+
lines.push("");
|
|
418
|
+
lines.push(header("OPTIONS"));
|
|
419
|
+
lines.push("");
|
|
420
|
+
lines.push(...formatOptionList(options));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
const examples = examplesMap.get(cmd);
|
|
424
|
+
if (examples && examples.length > 0) {
|
|
425
|
+
lines.push("");
|
|
426
|
+
lines.push(header("EXAMPLES"));
|
|
427
|
+
lines.push("");
|
|
428
|
+
for (const ex of examples) {
|
|
429
|
+
lines.push(` ${ex.description}:`);
|
|
430
|
+
lines.push(` $ ${ex.command}`);
|
|
431
|
+
lines.push("");
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
lines.push("");
|
|
435
|
+
lines.push(formatGlobalParameters(program2));
|
|
436
|
+
return lines.join("\n");
|
|
437
|
+
}
|
|
438
|
+
function showHelp(program2, args) {
|
|
439
|
+
if (args.length === 0) {
|
|
440
|
+
console.log(formatTopLevelHelp(program2));
|
|
441
|
+
return;
|
|
442
|
+
}
|
|
443
|
+
let current = program2;
|
|
444
|
+
const resolved = [];
|
|
445
|
+
for (let i = 0; i < args.length; i++) {
|
|
446
|
+
const found = findCommand(current, args[i]);
|
|
447
|
+
if (!found) {
|
|
448
|
+
const attempted = args.slice(0, i + 1).join(" ");
|
|
449
|
+
console.error(
|
|
450
|
+
chalk2.red(`Error: '${attempted}' is not a geonic command.`)
|
|
451
|
+
);
|
|
452
|
+
console.error(`
|
|
453
|
+
See 'geonic help' for available commands.`);
|
|
454
|
+
process.exit(1);
|
|
455
|
+
}
|
|
456
|
+
resolved.push(found);
|
|
457
|
+
current = found;
|
|
458
|
+
}
|
|
459
|
+
const target = resolved[resolved.length - 1];
|
|
460
|
+
const path = [program2.name(), ...resolved.map((c) => c.name())].join(" ");
|
|
461
|
+
console.log(formatCommandDetails(program2, target, path));
|
|
462
|
+
}
|
|
463
|
+
function registerHelpCommand(program2) {
|
|
464
|
+
program2.addHelpCommand(false);
|
|
465
|
+
program2.configureHelp({
|
|
466
|
+
formatHelp: (cmd, _helper) => {
|
|
467
|
+
const root = getRootProgram(cmd);
|
|
468
|
+
if (cmd === root) {
|
|
469
|
+
return formatTopLevelHelp(root);
|
|
470
|
+
}
|
|
471
|
+
const path = getCommandPath(cmd);
|
|
472
|
+
return formatCommandDetails(root, cmd, path);
|
|
473
|
+
}
|
|
474
|
+
});
|
|
475
|
+
program2.command("help").description("Get help on a specific command").argument("[args...]").allowUnknownOption().action((args) => {
|
|
476
|
+
showHelp(program2, args);
|
|
477
|
+
});
|
|
478
|
+
program2.argument("[command...]").action((commands) => {
|
|
479
|
+
if (commands.length > 0) {
|
|
480
|
+
console.error(
|
|
481
|
+
`geonic: '${commands[0]}' is not a geonic command. See 'geonic help'.`
|
|
482
|
+
);
|
|
483
|
+
process.exitCode = 1;
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
showHelp(program2, []);
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/commands/config.ts
|
|
491
|
+
var SENSITIVE_CONFIG_KEYS = /* @__PURE__ */ new Set(["token", "refreshToken", "apiKey"]);
|
|
492
|
+
function registerConfigCommand(program2) {
|
|
493
|
+
const config = program2.command("config").description("Manage CLI configuration");
|
|
494
|
+
const set = config.command("set").description("Save a config value").argument(
|
|
495
|
+
"<key>",
|
|
496
|
+
"Configuration key (url, service, token, refreshToken, format, apiKey)"
|
|
497
|
+
).argument("<value>", "Configuration value").action((...args) => {
|
|
498
|
+
const cmd = args[args.length - 1];
|
|
499
|
+
const key = args[0];
|
|
500
|
+
const value = args[1];
|
|
501
|
+
const profile = cmd.optsWithGlobals().profile;
|
|
502
|
+
setConfigValue(key, value, profile);
|
|
503
|
+
const display = SENSITIVE_CONFIG_KEYS.has(key) ? "***" : value;
|
|
504
|
+
printSuccess(`Set ${key} = ${display}`);
|
|
505
|
+
});
|
|
506
|
+
addExamples(set, [
|
|
507
|
+
{
|
|
508
|
+
description: "Set server URL",
|
|
509
|
+
command: "geonic config set url https://api.example.com"
|
|
510
|
+
},
|
|
511
|
+
{
|
|
512
|
+
description: "Set tenant (NGSILD-Tenant header)",
|
|
513
|
+
command: "geonic config set service my-tenant"
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
description: "Set authentication token",
|
|
517
|
+
command: "geonic config set token eyJhbGciOi..."
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
description: "Set API key",
|
|
521
|
+
command: "geonic config set apiKey your-api-key"
|
|
522
|
+
},
|
|
523
|
+
{
|
|
524
|
+
description: "Set default output format",
|
|
525
|
+
command: "geonic config set format table"
|
|
526
|
+
},
|
|
527
|
+
{
|
|
528
|
+
description: "Set config for a specific profile",
|
|
529
|
+
command: "geonic config set url https://staging.example.com --profile staging"
|
|
530
|
+
}
|
|
531
|
+
]);
|
|
532
|
+
const get = config.command("get").description("Get a config value").argument("<key>", "Configuration key").action((...args) => {
|
|
533
|
+
const cmd = args[args.length - 1];
|
|
534
|
+
const key = args[0];
|
|
535
|
+
const profile = cmd.optsWithGlobals().profile;
|
|
536
|
+
const value = getConfigValue(key, profile);
|
|
537
|
+
if (value === void 0) {
|
|
538
|
+
printInfo(`Key "${key}" is not set.`);
|
|
539
|
+
} else {
|
|
540
|
+
printOutput(value, "json");
|
|
541
|
+
}
|
|
542
|
+
});
|
|
543
|
+
addExamples(get, [
|
|
544
|
+
{
|
|
545
|
+
description: "Get server URL",
|
|
546
|
+
command: "geonic config get url"
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
description: "Get config value for a specific profile",
|
|
550
|
+
command: "geonic config get url --profile staging"
|
|
551
|
+
}
|
|
552
|
+
]);
|
|
553
|
+
const list = config.command("list").description("List all config values").action((...args) => {
|
|
554
|
+
const cmd = args[args.length - 1];
|
|
555
|
+
const profile = cmd.optsWithGlobals().profile;
|
|
556
|
+
const all = loadConfig(profile);
|
|
557
|
+
if (Object.keys(all).length === 0) {
|
|
558
|
+
printInfo(`No configuration set. Config path: ${getConfigPath()}`);
|
|
559
|
+
} else {
|
|
560
|
+
printOutput(all, "json");
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
addExamples(list, [
|
|
564
|
+
{
|
|
565
|
+
description: "List all configuration values",
|
|
566
|
+
command: "geonic config list"
|
|
567
|
+
}
|
|
568
|
+
]);
|
|
569
|
+
const del = config.command("delete").description("Delete a config value").argument("<key>", "Configuration key").action((...args) => {
|
|
570
|
+
const cmd = args[args.length - 1];
|
|
571
|
+
const key = args[0];
|
|
572
|
+
const profile = cmd.optsWithGlobals().profile;
|
|
573
|
+
deleteConfigValue(key, profile);
|
|
574
|
+
printSuccess(`Deleted key "${key}".`);
|
|
575
|
+
});
|
|
576
|
+
addExamples(del, [
|
|
577
|
+
{
|
|
578
|
+
description: "Delete a config value",
|
|
579
|
+
command: "geonic config delete url"
|
|
580
|
+
}
|
|
581
|
+
]);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// src/commands/auth.ts
|
|
585
|
+
import { Command } from "commander";
|
|
586
|
+
|
|
587
|
+
// src/client.ts
|
|
588
|
+
var GdbClient = class _GdbClient {
|
|
589
|
+
baseUrl;
|
|
590
|
+
service;
|
|
591
|
+
token;
|
|
592
|
+
refreshToken;
|
|
593
|
+
apiKey;
|
|
594
|
+
onTokenRefresh;
|
|
595
|
+
verbose;
|
|
596
|
+
refreshPromise;
|
|
597
|
+
constructor(options) {
|
|
598
|
+
this.baseUrl = options.baseUrl.replace(/\/+$/, "");
|
|
599
|
+
this.service = options.service;
|
|
600
|
+
this.token = options.token;
|
|
601
|
+
this.refreshToken = options.refreshToken;
|
|
602
|
+
this.apiKey = options.apiKey;
|
|
603
|
+
this.onTokenRefresh = options.onTokenRefresh;
|
|
604
|
+
this.verbose = options.verbose ?? false;
|
|
605
|
+
}
|
|
606
|
+
buildHeaders(extra) {
|
|
607
|
+
const headers = {};
|
|
608
|
+
headers["Content-Type"] = "application/ld+json";
|
|
609
|
+
headers["Accept"] = "application/ld+json";
|
|
610
|
+
if (this.service) headers["NGSILD-Tenant"] = this.service;
|
|
611
|
+
if (this.token) {
|
|
612
|
+
headers["Authorization"] = `Bearer ${this.token}`;
|
|
613
|
+
} else if (this.apiKey) {
|
|
614
|
+
headers["Authorization"] = `Bearer ${this.apiKey}`;
|
|
615
|
+
}
|
|
616
|
+
if (extra) {
|
|
617
|
+
Object.assign(headers, extra);
|
|
618
|
+
}
|
|
619
|
+
return headers;
|
|
620
|
+
}
|
|
621
|
+
buildUrl(path, params) {
|
|
622
|
+
const url = new URL(path, this.baseUrl);
|
|
623
|
+
if (params) {
|
|
624
|
+
for (const [key, value] of Object.entries(params)) {
|
|
625
|
+
if (value !== void 0 && value !== "") {
|
|
626
|
+
url.searchParams.set(key, value);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
return url.toString();
|
|
631
|
+
}
|
|
632
|
+
getBasePath() {
|
|
633
|
+
return "/ngsi-ld/v1";
|
|
634
|
+
}
|
|
635
|
+
static SENSITIVE_HEADERS = /* @__PURE__ */ new Set(["authorization"]);
|
|
636
|
+
static SENSITIVE_BODY_KEYS = /* @__PURE__ */ new Set([
|
|
637
|
+
"password",
|
|
638
|
+
"refreshToken",
|
|
639
|
+
"token",
|
|
640
|
+
"client_secret",
|
|
641
|
+
"clientSecret"
|
|
642
|
+
]);
|
|
643
|
+
logRequest(method, url, headers, body) {
|
|
644
|
+
if (!this.verbose) return;
|
|
645
|
+
process.stderr.write(`> ${method} ${url}
|
|
646
|
+
`);
|
|
647
|
+
for (const [k, v] of Object.entries(headers)) {
|
|
648
|
+
if (_GdbClient.SENSITIVE_HEADERS.has(k.toLowerCase())) {
|
|
649
|
+
process.stderr.write(`> ${k}: ***
|
|
650
|
+
`);
|
|
651
|
+
} else {
|
|
652
|
+
process.stderr.write(`> ${k}: ${v}
|
|
653
|
+
`);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
if (body) {
|
|
657
|
+
process.stderr.write(`> Body: ${_GdbClient.maskBodySecrets(body)}
|
|
658
|
+
`);
|
|
659
|
+
}
|
|
660
|
+
process.stderr.write("\n");
|
|
661
|
+
}
|
|
662
|
+
static maskBodySecrets(raw) {
|
|
663
|
+
try {
|
|
664
|
+
const obj = JSON.parse(raw);
|
|
665
|
+
for (const key of Object.keys(obj)) {
|
|
666
|
+
if (_GdbClient.SENSITIVE_BODY_KEYS.has(key)) {
|
|
667
|
+
obj[key] = "***";
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
return JSON.stringify(obj);
|
|
671
|
+
} catch {
|
|
672
|
+
return raw;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
logResponse(response) {
|
|
676
|
+
if (!this.verbose) return;
|
|
677
|
+
process.stderr.write(`< ${response.status} ${response.statusText}
|
|
678
|
+
`);
|
|
679
|
+
response.headers.forEach((v, k) => {
|
|
680
|
+
process.stderr.write(`< ${k}: ${v}
|
|
681
|
+
`);
|
|
682
|
+
});
|
|
683
|
+
process.stderr.write("\n");
|
|
684
|
+
}
|
|
685
|
+
canRefresh() {
|
|
686
|
+
return !!this.refreshToken && !this.apiKey;
|
|
687
|
+
}
|
|
688
|
+
async performTokenRefresh() {
|
|
689
|
+
if (this.refreshPromise) return this.refreshPromise;
|
|
690
|
+
this.refreshPromise = this.doRefresh();
|
|
691
|
+
try {
|
|
692
|
+
return await this.refreshPromise;
|
|
693
|
+
} finally {
|
|
694
|
+
this.refreshPromise = void 0;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
async doRefresh() {
|
|
698
|
+
if (!this.refreshToken) return false;
|
|
699
|
+
try {
|
|
700
|
+
const url = this.buildUrl("/auth/refresh");
|
|
701
|
+
const response = await fetch(url, {
|
|
702
|
+
method: "POST",
|
|
703
|
+
headers: { "Content-Type": "application/json" },
|
|
704
|
+
body: JSON.stringify({ refreshToken: this.refreshToken })
|
|
705
|
+
});
|
|
706
|
+
if (!response.ok) return false;
|
|
707
|
+
const data = await response.json();
|
|
708
|
+
const newToken = data.accessToken ?? data.token;
|
|
709
|
+
const newRefreshToken = data.refreshToken;
|
|
710
|
+
if (!newToken) return false;
|
|
711
|
+
this.token = newToken;
|
|
712
|
+
if (newRefreshToken) this.refreshToken = newRefreshToken;
|
|
713
|
+
this.onTokenRefresh?.(newToken, newRefreshToken);
|
|
714
|
+
return true;
|
|
715
|
+
} catch {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
async executeRequest(method, path, options) {
|
|
720
|
+
const url = this.buildUrl(`${this.getBasePath()}${path}`, options?.params);
|
|
721
|
+
const headers = this.buildHeaders(options?.headers);
|
|
722
|
+
const body = options?.body ? JSON.stringify(options.body) : void 0;
|
|
723
|
+
this.logRequest(method, url, headers, body);
|
|
724
|
+
const response = await fetch(url, { method, headers, body });
|
|
725
|
+
this.logResponse(response);
|
|
726
|
+
const countHeader = response.headers.get("NGSILD-Results-Count");
|
|
727
|
+
const count = countHeader ? parseInt(countHeader, 10) : void 0;
|
|
728
|
+
let data;
|
|
729
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
730
|
+
const text = await response.text();
|
|
731
|
+
if (text && (contentType.includes("json") || contentType.includes("ld+json"))) {
|
|
732
|
+
data = JSON.parse(text);
|
|
733
|
+
} else {
|
|
734
|
+
data = text;
|
|
735
|
+
}
|
|
736
|
+
if (!response.ok) {
|
|
737
|
+
const err = data;
|
|
738
|
+
const message = err?.description || err?.detail || err?.error || err?.title || `HTTP ${response.status}`;
|
|
739
|
+
throw new GdbClientError(message, response.status, err);
|
|
740
|
+
}
|
|
741
|
+
return { status: response.status, headers: response.headers, data, count };
|
|
742
|
+
}
|
|
743
|
+
async executeRawRequest(method, path, options) {
|
|
744
|
+
const url = this.buildUrl(path, options?.params);
|
|
745
|
+
const headers = this.buildHeaders(options?.headers);
|
|
746
|
+
const body = options?.body ? JSON.stringify(options.body) : void 0;
|
|
747
|
+
this.logRequest(method, url, headers, body);
|
|
748
|
+
const response = await fetch(url, { method, headers, body });
|
|
749
|
+
this.logResponse(response);
|
|
750
|
+
let data;
|
|
751
|
+
const contentType = response.headers.get("content-type") ?? "";
|
|
752
|
+
const text = await response.text();
|
|
753
|
+
if (text && (contentType.includes("json") || contentType.includes("ld+json"))) {
|
|
754
|
+
data = JSON.parse(text);
|
|
755
|
+
} else {
|
|
756
|
+
data = text;
|
|
757
|
+
}
|
|
758
|
+
if (!response.ok) {
|
|
759
|
+
const err = data;
|
|
760
|
+
const message = err?.description || err?.detail || err?.error || err?.title || `HTTP ${response.status}`;
|
|
761
|
+
throw new GdbClientError(message, response.status, err);
|
|
762
|
+
}
|
|
763
|
+
return { status: response.status, headers: response.headers, data };
|
|
764
|
+
}
|
|
765
|
+
async request(method, path, options) {
|
|
766
|
+
try {
|
|
767
|
+
return await this.executeRequest(method, path, options);
|
|
768
|
+
} catch (err) {
|
|
769
|
+
if (err instanceof GdbClientError && err.status === 401 && this.canRefresh()) {
|
|
770
|
+
const refreshed = await this.performTokenRefresh();
|
|
771
|
+
if (refreshed) {
|
|
772
|
+
return await this.executeRequest(method, path, options);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
throw err;
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
async get(path, params, headers) {
|
|
779
|
+
return this.request("GET", path, { params, headers });
|
|
780
|
+
}
|
|
781
|
+
async post(path, body, params) {
|
|
782
|
+
return this.request("POST", path, { body, params });
|
|
783
|
+
}
|
|
784
|
+
async patch(path, body, params) {
|
|
785
|
+
return this.request("PATCH", path, { body, params });
|
|
786
|
+
}
|
|
787
|
+
async put(path, body, params) {
|
|
788
|
+
return this.request("PUT", path, { body, params });
|
|
789
|
+
}
|
|
790
|
+
async delete(path, params) {
|
|
791
|
+
return this.request("DELETE", path, { params });
|
|
792
|
+
}
|
|
793
|
+
/** Make a request to a raw URL path (not prefixed with API base path) */
|
|
794
|
+
async rawRequest(method, path, options) {
|
|
795
|
+
try {
|
|
796
|
+
return await this.executeRawRequest(method, path, options);
|
|
797
|
+
} catch (err) {
|
|
798
|
+
if (err instanceof GdbClientError && err.status === 401 && this.canRefresh()) {
|
|
799
|
+
const refreshed = await this.performTokenRefresh();
|
|
800
|
+
if (refreshed) {
|
|
801
|
+
return await this.executeRawRequest(method, path, options);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
throw err;
|
|
805
|
+
}
|
|
806
|
+
}
|
|
807
|
+
};
|
|
808
|
+
var GdbClientError = class extends Error {
|
|
809
|
+
constructor(message, status, ngsiError) {
|
|
810
|
+
super(message);
|
|
811
|
+
this.status = status;
|
|
812
|
+
this.ngsiError = ngsiError;
|
|
813
|
+
this.name = "GdbClientError";
|
|
814
|
+
}
|
|
815
|
+
};
|
|
816
|
+
|
|
817
|
+
// src/helpers.ts
|
|
818
|
+
function resolveOptions(cmd) {
|
|
819
|
+
const opts = cmd.optsWithGlobals();
|
|
820
|
+
const config = loadConfig(opts.profile);
|
|
821
|
+
return {
|
|
822
|
+
url: opts.url ?? config.url,
|
|
823
|
+
service: opts.service ?? config.service,
|
|
824
|
+
token: opts.token ?? config.token,
|
|
825
|
+
format: opts.format ?? config.format ?? "json",
|
|
826
|
+
color: opts.color,
|
|
827
|
+
verbose: opts.verbose,
|
|
828
|
+
profile: opts.profile,
|
|
829
|
+
apiKey: opts.apiKey ?? process.env.GDB_API_KEY ?? config.apiKey
|
|
830
|
+
};
|
|
831
|
+
}
|
|
832
|
+
function createClient(cmd) {
|
|
833
|
+
const opts = resolveOptions(cmd);
|
|
834
|
+
if (!opts.url) {
|
|
835
|
+
printError("No URL configured. Use `geonic config set url <url>` or pass --url.");
|
|
836
|
+
process.exit(1);
|
|
837
|
+
}
|
|
838
|
+
opts.url = validateUrl(opts.url);
|
|
839
|
+
const cliOpts = cmd.optsWithGlobals();
|
|
840
|
+
const usingCliToken = !!cliOpts.token;
|
|
841
|
+
const config = loadConfig(opts.profile);
|
|
842
|
+
return new GdbClient({
|
|
843
|
+
baseUrl: opts.url,
|
|
844
|
+
service: opts.service,
|
|
845
|
+
token: opts.token,
|
|
846
|
+
refreshToken: usingCliToken ? void 0 : config.refreshToken,
|
|
847
|
+
apiKey: opts.apiKey,
|
|
848
|
+
onTokenRefresh: usingCliToken ? void 0 : (token, refreshToken) => {
|
|
849
|
+
const cfg = loadConfig(opts.profile);
|
|
850
|
+
cfg.token = token;
|
|
851
|
+
if (refreshToken) cfg.refreshToken = refreshToken;
|
|
852
|
+
saveConfig(cfg, opts.profile);
|
|
853
|
+
},
|
|
854
|
+
verbose: opts.verbose
|
|
855
|
+
});
|
|
856
|
+
}
|
|
857
|
+
function getFormat(cmd) {
|
|
858
|
+
const opts = resolveOptions(cmd);
|
|
859
|
+
return opts.format;
|
|
860
|
+
}
|
|
861
|
+
function outputResponse(response, format, showCount) {
|
|
862
|
+
if (showCount && response.count !== void 0) {
|
|
863
|
+
printCount(response.count);
|
|
864
|
+
}
|
|
865
|
+
if (response.data !== void 0 && response.data !== "") {
|
|
866
|
+
printOutput(response.data, format);
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
function withErrorHandler(fn) {
|
|
870
|
+
return async (...args) => {
|
|
871
|
+
try {
|
|
872
|
+
await fn(...args);
|
|
873
|
+
} catch (err) {
|
|
874
|
+
if (err instanceof GdbClientError && err.status === 401) {
|
|
875
|
+
printError("Authentication failed. Please run `geonic login` to re-authenticate.");
|
|
876
|
+
} else if (err instanceof Error) {
|
|
877
|
+
printError(err.message);
|
|
878
|
+
} else {
|
|
879
|
+
printError(String(err));
|
|
880
|
+
}
|
|
881
|
+
process.exit(1);
|
|
882
|
+
}
|
|
883
|
+
};
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
// src/prompt.ts
|
|
887
|
+
import { createInterface } from "readline/promises";
|
|
888
|
+
function isInteractive() {
|
|
889
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
890
|
+
}
|
|
891
|
+
async function promptEmail() {
|
|
892
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
893
|
+
try {
|
|
894
|
+
const email = await rl.question("Email: ");
|
|
895
|
+
return email.trim();
|
|
896
|
+
} finally {
|
|
897
|
+
rl.close();
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
async function promptPassword() {
|
|
901
|
+
return new Promise((resolve, reject) => {
|
|
902
|
+
const stdin = process.stdin;
|
|
903
|
+
const stdout = process.stdout;
|
|
904
|
+
stdout.write("Password: ");
|
|
905
|
+
if (!stdin.isTTY) {
|
|
906
|
+
let data = "";
|
|
907
|
+
stdin.setEncoding("utf-8");
|
|
908
|
+
stdin.resume();
|
|
909
|
+
const cleanup = () => {
|
|
910
|
+
stdin.removeListener("data", onData2);
|
|
911
|
+
stdin.removeListener("end", onEnd);
|
|
912
|
+
stdin.removeListener("error", onError2);
|
|
913
|
+
stdin.pause();
|
|
914
|
+
};
|
|
915
|
+
const onData2 = (chunk) => {
|
|
916
|
+
const newline = chunk.indexOf("\n");
|
|
917
|
+
if (newline !== -1) {
|
|
918
|
+
data += chunk.slice(0, newline);
|
|
919
|
+
cleanup();
|
|
920
|
+
stdout.write("\n");
|
|
921
|
+
resolve(data);
|
|
922
|
+
} else {
|
|
923
|
+
data += chunk;
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
const onEnd = () => {
|
|
927
|
+
cleanup();
|
|
928
|
+
stdout.write("\n");
|
|
929
|
+
resolve(data);
|
|
930
|
+
};
|
|
931
|
+
const onError2 = (err) => {
|
|
932
|
+
cleanup();
|
|
933
|
+
reject(err);
|
|
934
|
+
};
|
|
935
|
+
stdin.on("data", onData2);
|
|
936
|
+
stdin.on("end", onEnd);
|
|
937
|
+
stdin.on("error", onError2);
|
|
938
|
+
return;
|
|
939
|
+
}
|
|
940
|
+
const wasRaw = stdin.isRaw;
|
|
941
|
+
stdin.setRawMode(true);
|
|
942
|
+
stdin.resume();
|
|
943
|
+
stdin.setEncoding("utf-8");
|
|
944
|
+
let password = "";
|
|
945
|
+
const restoreTerminal = () => {
|
|
946
|
+
stdin.removeListener("data", onData);
|
|
947
|
+
stdin.removeListener("error", onError);
|
|
948
|
+
stdin.setRawMode(wasRaw ?? false);
|
|
949
|
+
stdin.pause();
|
|
950
|
+
};
|
|
951
|
+
const onError = (err) => {
|
|
952
|
+
restoreTerminal();
|
|
953
|
+
stdout.write("\n");
|
|
954
|
+
reject(err);
|
|
955
|
+
};
|
|
956
|
+
const onData = (char) => {
|
|
957
|
+
const code = char.charCodeAt(0);
|
|
958
|
+
if (char === "\r" || char === "\n") {
|
|
959
|
+
restoreTerminal();
|
|
960
|
+
stdout.write("\n");
|
|
961
|
+
resolve(password);
|
|
962
|
+
} else if (code === 3) {
|
|
963
|
+
restoreTerminal();
|
|
964
|
+
stdout.write("\n");
|
|
965
|
+
reject(new Error("User cancelled"));
|
|
966
|
+
} else if (code === 127 || code === 8) {
|
|
967
|
+
if (password.length > 0) {
|
|
968
|
+
password = password.slice(0, -1);
|
|
969
|
+
stdout.write("\b \b");
|
|
970
|
+
}
|
|
971
|
+
} else if (code >= 32) {
|
|
972
|
+
password += char;
|
|
973
|
+
stdout.write("*");
|
|
974
|
+
}
|
|
975
|
+
};
|
|
976
|
+
stdin.on("data", onData);
|
|
977
|
+
stdin.on("error", onError);
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
|
|
981
|
+
// src/token.ts
|
|
982
|
+
function decodeJwtPayload(token) {
|
|
983
|
+
try {
|
|
984
|
+
const parts = token.split(".");
|
|
985
|
+
if (parts.length !== 3) return null;
|
|
986
|
+
const payload = parts[1];
|
|
987
|
+
const json = Buffer.from(payload, "base64").toString("utf-8");
|
|
988
|
+
return JSON.parse(json);
|
|
989
|
+
} catch {
|
|
990
|
+
return null;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
function getTokenStatus(token) {
|
|
994
|
+
const payload = decodeJwtPayload(token);
|
|
995
|
+
if (!payload || typeof payload.exp !== "number") {
|
|
996
|
+
return { expiresAt: null, isExpired: false, isExpiringSoon: false, remainingMs: null };
|
|
997
|
+
}
|
|
998
|
+
const expiresAt = new Date(payload.exp * 1e3);
|
|
999
|
+
const now = Date.now();
|
|
1000
|
+
const remainingMs = expiresAt.getTime() - now;
|
|
1001
|
+
const fiveMinutes = 5 * 60 * 1e3;
|
|
1002
|
+
return {
|
|
1003
|
+
expiresAt,
|
|
1004
|
+
isExpired: remainingMs <= 0,
|
|
1005
|
+
isExpiringSoon: remainingMs > 0 && remainingMs <= fiveMinutes,
|
|
1006
|
+
remainingMs
|
|
1007
|
+
};
|
|
1008
|
+
}
|
|
1009
|
+
function formatDuration(ms) {
|
|
1010
|
+
if (ms <= 0) return "expired";
|
|
1011
|
+
const seconds = Math.floor(ms / 1e3);
|
|
1012
|
+
const minutes = Math.floor(seconds / 60);
|
|
1013
|
+
const hours = Math.floor(minutes / 60);
|
|
1014
|
+
const days = Math.floor(hours / 24);
|
|
1015
|
+
const parts = [];
|
|
1016
|
+
if (days > 0) parts.push(`${days}d`);
|
|
1017
|
+
if (hours % 24 > 0) parts.push(`${hours % 24}h`);
|
|
1018
|
+
if (minutes % 60 > 0 && days === 0) parts.push(`${minutes % 60}m`);
|
|
1019
|
+
if (parts.length === 0) parts.push(`${seconds}s`);
|
|
1020
|
+
return parts.join(" ");
|
|
1021
|
+
}
|
|
1022
|
+
|
|
1023
|
+
// src/oauth.ts
|
|
1024
|
+
async function clientCredentialsGrant(options) {
|
|
1025
|
+
const url = new URL("/oauth/token", options.baseUrl).toString();
|
|
1026
|
+
const credentials = Buffer.from(`${options.clientId}:${options.clientSecret}`).toString("base64");
|
|
1027
|
+
const params = new URLSearchParams();
|
|
1028
|
+
params.set("grant_type", "client_credentials");
|
|
1029
|
+
if (options.scope) {
|
|
1030
|
+
params.set("scope", options.scope);
|
|
1031
|
+
}
|
|
1032
|
+
const response = await fetch(url, {
|
|
1033
|
+
method: "POST",
|
|
1034
|
+
headers: {
|
|
1035
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
1036
|
+
Authorization: `Basic ${credentials}`
|
|
1037
|
+
},
|
|
1038
|
+
body: params.toString()
|
|
1039
|
+
});
|
|
1040
|
+
if (!response.ok) {
|
|
1041
|
+
const text = await response.text();
|
|
1042
|
+
let message;
|
|
1043
|
+
try {
|
|
1044
|
+
const err = JSON.parse(text);
|
|
1045
|
+
message = err.error_description ?? err.error ?? text;
|
|
1046
|
+
} catch {
|
|
1047
|
+
message = text || `HTTP ${response.status}`;
|
|
1048
|
+
}
|
|
1049
|
+
throw new Error(`OAuth token request failed: ${message}`);
|
|
1050
|
+
}
|
|
1051
|
+
return await response.json();
|
|
1052
|
+
}
|
|
1053
|
+
|
|
1054
|
+
// src/commands/auth.ts
|
|
1055
|
+
import chalk3 from "chalk";
|
|
1056
|
+
function createLoginCommand() {
|
|
1057
|
+
return new Command("login").description("Authenticate and save token").option("--client-credentials", "Use OAuth 2.0 Client Credentials flow").option("--client-id <id>", "OAuth client ID").option("--client-secret <secret>", "OAuth client secret").option("--scope <scopes>", "OAuth scopes (space-separated)").option("--tenant-id <id>", "Tenant ID for scoped authentication").action(
|
|
1058
|
+
withErrorHandler(async (...args) => {
|
|
1059
|
+
const cmd = args[args.length - 1];
|
|
1060
|
+
const loginOpts = cmd.opts();
|
|
1061
|
+
const globalOpts = resolveOptions(cmd);
|
|
1062
|
+
if (loginOpts.clientCredentials) {
|
|
1063
|
+
const clientId = loginOpts.clientId ?? process.env.GDB_OAUTH_CLIENT_ID;
|
|
1064
|
+
const clientSecret = loginOpts.clientSecret ?? process.env.GDB_OAUTH_CLIENT_SECRET;
|
|
1065
|
+
if (!clientId || !clientSecret) {
|
|
1066
|
+
printError(
|
|
1067
|
+
"Client ID and secret are required. Use --client-id/--client-secret or GDB_OAUTH_CLIENT_ID/GDB_OAUTH_CLIENT_SECRET."
|
|
1068
|
+
);
|
|
1069
|
+
process.exit(1);
|
|
1070
|
+
}
|
|
1071
|
+
if (!globalOpts.url) {
|
|
1072
|
+
printError("No URL configured. Use `geonic config set url <url>` or pass --url.");
|
|
1073
|
+
process.exit(1);
|
|
1074
|
+
}
|
|
1075
|
+
const result = await clientCredentialsGrant({
|
|
1076
|
+
baseUrl: globalOpts.url,
|
|
1077
|
+
clientId,
|
|
1078
|
+
clientSecret,
|
|
1079
|
+
scope: loginOpts.scope
|
|
1080
|
+
});
|
|
1081
|
+
const config2 = loadConfig(globalOpts.profile);
|
|
1082
|
+
config2.token = result.access_token;
|
|
1083
|
+
delete config2.refreshToken;
|
|
1084
|
+
saveConfig(config2, globalOpts.profile);
|
|
1085
|
+
printSuccess("Login successful (OAuth Client Credentials). Token saved to config.");
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
let email = process.env.GDB_EMAIL;
|
|
1089
|
+
let password = process.env.GDB_PASSWORD;
|
|
1090
|
+
if (!email || !password) {
|
|
1091
|
+
if (isInteractive()) {
|
|
1092
|
+
if (!email) email = await promptEmail();
|
|
1093
|
+
if (!password) password = await promptPassword();
|
|
1094
|
+
} else {
|
|
1095
|
+
printError(
|
|
1096
|
+
"Set GDB_EMAIL and GDB_PASSWORD environment variables, or run in a terminal for interactive login."
|
|
1097
|
+
);
|
|
1098
|
+
process.exit(1);
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
const client = createClient(cmd);
|
|
1102
|
+
const body = { email, password };
|
|
1103
|
+
if (loginOpts.tenantId) {
|
|
1104
|
+
body.tenantId = loginOpts.tenantId;
|
|
1105
|
+
}
|
|
1106
|
+
const response = await client.rawRequest("POST", "/auth/login", { body });
|
|
1107
|
+
const data = response.data;
|
|
1108
|
+
const token = data.accessToken ?? data.token;
|
|
1109
|
+
const refreshToken = data.refreshToken;
|
|
1110
|
+
if (!token) {
|
|
1111
|
+
printError("No token received from server.");
|
|
1112
|
+
process.exit(1);
|
|
1113
|
+
}
|
|
1114
|
+
const config = loadConfig(globalOpts.profile);
|
|
1115
|
+
config.token = token;
|
|
1116
|
+
if (refreshToken) {
|
|
1117
|
+
config.refreshToken = refreshToken;
|
|
1118
|
+
} else {
|
|
1119
|
+
delete config.refreshToken;
|
|
1120
|
+
}
|
|
1121
|
+
saveConfig(config, globalOpts.profile);
|
|
1122
|
+
printSuccess("Login successful. Token saved to config.");
|
|
1123
|
+
})
|
|
1124
|
+
);
|
|
1125
|
+
}
|
|
1126
|
+
function createLogoutCommand() {
|
|
1127
|
+
return new Command("logout").description("Clear saved authentication token").action(
|
|
1128
|
+
withErrorHandler(async (...args) => {
|
|
1129
|
+
const cmd = args[args.length - 1];
|
|
1130
|
+
const globalOpts = resolveOptions(cmd);
|
|
1131
|
+
const config = loadConfig(globalOpts.profile);
|
|
1132
|
+
if (config.token && globalOpts.url) {
|
|
1133
|
+
try {
|
|
1134
|
+
const client = createClient(cmd);
|
|
1135
|
+
await client.rawRequest("POST", "/auth/logout");
|
|
1136
|
+
} catch {
|
|
1137
|
+
}
|
|
1138
|
+
}
|
|
1139
|
+
delete config.token;
|
|
1140
|
+
delete config.refreshToken;
|
|
1141
|
+
saveConfig(config, globalOpts.profile);
|
|
1142
|
+
printSuccess("Logged out. Token cleared from config.");
|
|
1143
|
+
})
|
|
1144
|
+
);
|
|
1145
|
+
}
|
|
1146
|
+
function createMeAction() {
|
|
1147
|
+
return withErrorHandler(async (...args) => {
|
|
1148
|
+
const cmd = args[args.length - 1];
|
|
1149
|
+
const globalOpts = resolveOptions(cmd);
|
|
1150
|
+
if (!globalOpts.token && !globalOpts.apiKey) {
|
|
1151
|
+
printInfo("Not logged in. Use `geonic auth login` to authenticate.");
|
|
1152
|
+
return;
|
|
1153
|
+
}
|
|
1154
|
+
const client = createClient(cmd);
|
|
1155
|
+
const format = getFormat(cmd);
|
|
1156
|
+
const response = await client.rawRequest("GET", "/me");
|
|
1157
|
+
outputResponse(response, format);
|
|
1158
|
+
if (format && format !== "table") {
|
|
1159
|
+
return;
|
|
1160
|
+
}
|
|
1161
|
+
const latestConfig = loadConfig(globalOpts.profile);
|
|
1162
|
+
if (latestConfig.token) {
|
|
1163
|
+
const status = getTokenStatus(latestConfig.token);
|
|
1164
|
+
if (status.expiresAt) {
|
|
1165
|
+
if (status.isExpired) {
|
|
1166
|
+
console.log(chalk3.red(`Token expires: ${status.expiresAt.toISOString()} (expired)`));
|
|
1167
|
+
} else if (status.isExpiringSoon) {
|
|
1168
|
+
printWarning(
|
|
1169
|
+
`Token expires: ${status.expiresAt.toISOString()} (${formatDuration(status.remainingMs)} remaining)`
|
|
1170
|
+
);
|
|
1171
|
+
} else {
|
|
1172
|
+
printInfo(
|
|
1173
|
+
`Token expires: ${status.expiresAt.toISOString()} (${formatDuration(status.remainingMs)} remaining)`
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
const profileName = globalOpts.profile ?? getCurrentProfile();
|
|
1179
|
+
printInfo(`Profile: ${profileName}`);
|
|
1180
|
+
});
|
|
1181
|
+
}
|
|
1182
|
+
function registerAuthCommands(program2) {
|
|
1183
|
+
const auth = program2.command("auth").description("Manage authentication");
|
|
1184
|
+
const login = createLoginCommand();
|
|
1185
|
+
addExamples(login, [
|
|
1186
|
+
{
|
|
1187
|
+
description: "Interactive login (email/password prompt)",
|
|
1188
|
+
command: "geonic auth login"
|
|
1189
|
+
},
|
|
1190
|
+
{
|
|
1191
|
+
description: "Login with OAuth client credentials",
|
|
1192
|
+
command: "geonic auth login --client-credentials --client-id MY_ID --client-secret MY_SECRET"
|
|
1193
|
+
},
|
|
1194
|
+
{
|
|
1195
|
+
description: "Login with environment variables",
|
|
1196
|
+
command: "GDB_EMAIL=user@example.com GDB_PASSWORD=pass geonic auth login"
|
|
1197
|
+
},
|
|
1198
|
+
{
|
|
1199
|
+
description: "Login to a specific tenant",
|
|
1200
|
+
command: "geonic auth login --tenant-id my-tenant"
|
|
1201
|
+
}
|
|
1202
|
+
]);
|
|
1203
|
+
auth.addCommand(login);
|
|
1204
|
+
const logout = createLogoutCommand();
|
|
1205
|
+
addExamples(logout, [
|
|
1206
|
+
{
|
|
1207
|
+
description: "Clear saved authentication token",
|
|
1208
|
+
command: "geonic auth logout"
|
|
1209
|
+
}
|
|
1210
|
+
]);
|
|
1211
|
+
auth.addCommand(logout);
|
|
1212
|
+
const me = program2.command("me").description("Display current authenticated user").action(createMeAction());
|
|
1213
|
+
addExamples(me, [
|
|
1214
|
+
{
|
|
1215
|
+
description: "Show current user info",
|
|
1216
|
+
command: "geonic me"
|
|
1217
|
+
}
|
|
1218
|
+
]);
|
|
1219
|
+
program2.addCommand(createLoginCommand(), { hidden: true });
|
|
1220
|
+
program2.addCommand(createLogoutCommand(), { hidden: true });
|
|
1221
|
+
const hiddenWhoami = new Command("whoami").description("Display current authenticated user").action(createMeAction());
|
|
1222
|
+
program2.addCommand(hiddenWhoami, { hidden: true });
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
// src/commands/profile.ts
|
|
1226
|
+
function registerProfileCommands(program2) {
|
|
1227
|
+
const profile = program2.command("profile").description("Manage connection profiles");
|
|
1228
|
+
const list = profile.command("list").description("List all profiles").action(() => {
|
|
1229
|
+
const profiles = listProfiles();
|
|
1230
|
+
for (const p of profiles) {
|
|
1231
|
+
const marker = p.active ? " *" : "";
|
|
1232
|
+
console.log(`${p.name}${marker}`);
|
|
1233
|
+
}
|
|
1234
|
+
});
|
|
1235
|
+
addExamples(list, [
|
|
1236
|
+
{
|
|
1237
|
+
description: "List all profiles (active profile marked with *)",
|
|
1238
|
+
command: "geonic profile list"
|
|
1239
|
+
}
|
|
1240
|
+
]);
|
|
1241
|
+
const use = profile.command("use <name>").description("Switch active profile").action((name) => {
|
|
1242
|
+
try {
|
|
1243
|
+
setCurrentProfile(name);
|
|
1244
|
+
printSuccess(`Switched to profile "${name}".`);
|
|
1245
|
+
} catch (err) {
|
|
1246
|
+
printError(err.message);
|
|
1247
|
+
process.exit(1);
|
|
1248
|
+
}
|
|
1249
|
+
});
|
|
1250
|
+
addExamples(use, [
|
|
1251
|
+
{
|
|
1252
|
+
description: "Switch to staging profile",
|
|
1253
|
+
command: "geonic profile use staging"
|
|
1254
|
+
}
|
|
1255
|
+
]);
|
|
1256
|
+
const profileCreate = profile.command("create <name>").description("Create a new profile").action((name) => {
|
|
1257
|
+
try {
|
|
1258
|
+
createProfile(name);
|
|
1259
|
+
printSuccess(`Profile "${name}" created.`);
|
|
1260
|
+
} catch (err) {
|
|
1261
|
+
printError(err.message);
|
|
1262
|
+
process.exit(1);
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
addExamples(profileCreate, [
|
|
1266
|
+
{
|
|
1267
|
+
description: "Create a new profile for staging",
|
|
1268
|
+
command: "geonic profile create staging"
|
|
1269
|
+
}
|
|
1270
|
+
]);
|
|
1271
|
+
const del = profile.command("delete <name>").description("Delete a profile").action((name) => {
|
|
1272
|
+
try {
|
|
1273
|
+
deleteProfile(name);
|
|
1274
|
+
printSuccess(`Profile "${name}" deleted.`);
|
|
1275
|
+
} catch (err) {
|
|
1276
|
+
printError(err.message);
|
|
1277
|
+
process.exit(1);
|
|
1278
|
+
}
|
|
1279
|
+
});
|
|
1280
|
+
addExamples(del, [
|
|
1281
|
+
{
|
|
1282
|
+
description: "Delete a profile",
|
|
1283
|
+
command: "geonic profile delete staging"
|
|
1284
|
+
}
|
|
1285
|
+
]);
|
|
1286
|
+
const show = profile.command("show [name]").description("Show profile settings").action((name) => {
|
|
1287
|
+
const profileName = name ?? getCurrentProfile();
|
|
1288
|
+
const config = loadConfig(profileName);
|
|
1289
|
+
const entries = Object.entries(config).filter(([, v]) => v !== void 0);
|
|
1290
|
+
if (entries.length === 0) {
|
|
1291
|
+
printInfo(`Profile "${profileName}" has no settings.`);
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
for (const [key, value] of entries) {
|
|
1295
|
+
if ((key === "token" || key === "refreshToken" || key === "apiKey") && typeof value === "string") {
|
|
1296
|
+
console.log(`${key}: ***`);
|
|
1297
|
+
} else {
|
|
1298
|
+
console.log(`${key}: ${value}`);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
});
|
|
1302
|
+
addExamples(show, [
|
|
1303
|
+
{
|
|
1304
|
+
description: "Show current profile settings",
|
|
1305
|
+
command: "geonic profile show"
|
|
1306
|
+
},
|
|
1307
|
+
{
|
|
1308
|
+
description: "Show settings for a specific profile",
|
|
1309
|
+
command: "geonic profile show production"
|
|
1310
|
+
}
|
|
1311
|
+
]);
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// src/input.ts
|
|
1315
|
+
import JSON5 from "json5";
|
|
1316
|
+
import { readFileSync as readFileSync2 } from "fs";
|
|
1317
|
+
import { createInterface as createInterface2 } from "readline";
|
|
1318
|
+
async function parseJsonInput(input) {
|
|
1319
|
+
if (input !== void 0) {
|
|
1320
|
+
if (input === "-") return parseData(readFileSync2(0, "utf-8"));
|
|
1321
|
+
if (input.startsWith("@")) return parseData(readFileSync2(input.slice(1), "utf-8"));
|
|
1322
|
+
return parseData(input);
|
|
1323
|
+
}
|
|
1324
|
+
if (!process.stdin.isTTY) {
|
|
1325
|
+
return parseData(readFileSync2(0, "utf-8"));
|
|
1326
|
+
}
|
|
1327
|
+
return readInteractiveJson();
|
|
1328
|
+
}
|
|
1329
|
+
function parseData(text) {
|
|
1330
|
+
return JSON5.parse(text.trim());
|
|
1331
|
+
}
|
|
1332
|
+
async function readInteractiveJson() {
|
|
1333
|
+
const rl = createInterface2({
|
|
1334
|
+
input: process.stdin,
|
|
1335
|
+
output: process.stderr,
|
|
1336
|
+
prompt: "json> "
|
|
1337
|
+
});
|
|
1338
|
+
process.stderr.write("Enter JSON (auto-submits when braces close, Ctrl+C to cancel):\n");
|
|
1339
|
+
rl.prompt();
|
|
1340
|
+
const lines = [];
|
|
1341
|
+
let depth = 0;
|
|
1342
|
+
let started = false;
|
|
1343
|
+
let inBlockComment = false;
|
|
1344
|
+
let inString = false;
|
|
1345
|
+
let stringChar = "";
|
|
1346
|
+
let cancelled = false;
|
|
1347
|
+
return new Promise((resolve, reject) => {
|
|
1348
|
+
rl.on("SIGINT", () => {
|
|
1349
|
+
cancelled = true;
|
|
1350
|
+
rl.close();
|
|
1351
|
+
});
|
|
1352
|
+
rl.on("line", (line) => {
|
|
1353
|
+
lines.push(line);
|
|
1354
|
+
const result = trackDepth(line, depth, started, inBlockComment, inString, stringChar);
|
|
1355
|
+
depth = result.depth;
|
|
1356
|
+
started = result.started;
|
|
1357
|
+
inBlockComment = result.inBlockComment;
|
|
1358
|
+
inString = result.inString;
|
|
1359
|
+
stringChar = result.stringChar;
|
|
1360
|
+
if (started && depth <= 0 && !inBlockComment && !inString) {
|
|
1361
|
+
rl.close();
|
|
1362
|
+
try {
|
|
1363
|
+
resolve(parseData(lines.join("\n")));
|
|
1364
|
+
} catch (err) {
|
|
1365
|
+
reject(err);
|
|
1366
|
+
}
|
|
1367
|
+
} else {
|
|
1368
|
+
rl.setPrompt("... ");
|
|
1369
|
+
rl.prompt();
|
|
1370
|
+
}
|
|
1371
|
+
});
|
|
1372
|
+
rl.on("close", () => {
|
|
1373
|
+
if (cancelled) {
|
|
1374
|
+
reject(new Error("Input cancelled."));
|
|
1375
|
+
return;
|
|
1376
|
+
}
|
|
1377
|
+
if (lines.length > 0 && (!started || depth > 0 || inBlockComment || inString)) {
|
|
1378
|
+
try {
|
|
1379
|
+
resolve(parseData(lines.join("\n")));
|
|
1380
|
+
} catch (err) {
|
|
1381
|
+
reject(err);
|
|
1382
|
+
}
|
|
1383
|
+
} else if (lines.length === 0) {
|
|
1384
|
+
reject(new Error("No input provided."));
|
|
1385
|
+
}
|
|
1386
|
+
});
|
|
1387
|
+
});
|
|
1388
|
+
}
|
|
1389
|
+
function trackDepth(line, depth, started, inBlockComment, inString, stringChar) {
|
|
1390
|
+
for (let i = 0; i < line.length; i++) {
|
|
1391
|
+
const ch = line[i];
|
|
1392
|
+
const next = i + 1 < line.length ? line[i + 1] : "";
|
|
1393
|
+
if (inBlockComment) {
|
|
1394
|
+
if (ch === "*" && next === "/") {
|
|
1395
|
+
inBlockComment = false;
|
|
1396
|
+
i++;
|
|
1397
|
+
}
|
|
1398
|
+
continue;
|
|
1399
|
+
}
|
|
1400
|
+
if (inString) {
|
|
1401
|
+
if (ch === "\\" && i + 1 < line.length) {
|
|
1402
|
+
i++;
|
|
1403
|
+
} else if (ch === stringChar) {
|
|
1404
|
+
inString = false;
|
|
1405
|
+
}
|
|
1406
|
+
continue;
|
|
1407
|
+
}
|
|
1408
|
+
if (ch === "/" && next === "/") break;
|
|
1409
|
+
if (ch === "/" && next === "*") {
|
|
1410
|
+
inBlockComment = true;
|
|
1411
|
+
i++;
|
|
1412
|
+
continue;
|
|
1413
|
+
}
|
|
1414
|
+
if (ch === '"' || ch === "'") {
|
|
1415
|
+
inString = true;
|
|
1416
|
+
stringChar = ch;
|
|
1417
|
+
} else if (ch === "{" || ch === "[") {
|
|
1418
|
+
depth++;
|
|
1419
|
+
started = true;
|
|
1420
|
+
} else if (ch === "}" || ch === "]") {
|
|
1421
|
+
depth--;
|
|
1422
|
+
}
|
|
1423
|
+
}
|
|
1424
|
+
return { depth, started, inBlockComment, inString, stringChar };
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
// src/commands/attrs.ts
|
|
1428
|
+
function addAttrsSubcommands(attrs) {
|
|
1429
|
+
const list = attrs.command("list").description("List all attributes of an entity").argument("<entityId>", "Entity ID").action(
|
|
1430
|
+
withErrorHandler(
|
|
1431
|
+
async (entityId, _opts, cmd) => {
|
|
1432
|
+
const client = createClient(cmd);
|
|
1433
|
+
const format = getFormat(cmd);
|
|
1434
|
+
const response = await client.get(
|
|
1435
|
+
`/entities/${encodeURIComponent(entityId)}/attrs`
|
|
1436
|
+
);
|
|
1437
|
+
outputResponse(response, format);
|
|
1438
|
+
}
|
|
1439
|
+
)
|
|
1440
|
+
);
|
|
1441
|
+
addExamples(list, [
|
|
1442
|
+
{
|
|
1443
|
+
description: "List all attributes of an entity",
|
|
1444
|
+
command: "geonic entities attrs list urn:ngsi-ld:Sensor:001"
|
|
1445
|
+
}
|
|
1446
|
+
]);
|
|
1447
|
+
const get = attrs.command("get").description("Get a specific attribute of an entity").argument("<entityId>", "Entity ID").argument("<attrName>", "Attribute name").action(
|
|
1448
|
+
withErrorHandler(
|
|
1449
|
+
async (entityId, attrName, _opts, cmd) => {
|
|
1450
|
+
const client = createClient(cmd);
|
|
1451
|
+
const format = getFormat(cmd);
|
|
1452
|
+
const response = await client.get(
|
|
1453
|
+
`/entities/${encodeURIComponent(entityId)}/attrs/${encodeURIComponent(attrName)}`
|
|
1454
|
+
);
|
|
1455
|
+
outputResponse(response, format);
|
|
1456
|
+
}
|
|
1457
|
+
)
|
|
1458
|
+
);
|
|
1459
|
+
addExamples(get, [
|
|
1460
|
+
{
|
|
1461
|
+
description: "Get a specific attribute",
|
|
1462
|
+
command: "geonic entities attrs get urn:ngsi-ld:Sensor:001 temperature"
|
|
1463
|
+
}
|
|
1464
|
+
]);
|
|
1465
|
+
const add = attrs.command("add").description("Add attributes to an entity").argument("<entityId>", "Entity ID").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
1466
|
+
withErrorHandler(
|
|
1467
|
+
async (entityId, json, _opts, cmd) => {
|
|
1468
|
+
const client = createClient(cmd);
|
|
1469
|
+
const data = await parseJsonInput(json);
|
|
1470
|
+
await client.post(
|
|
1471
|
+
`/entities/${encodeURIComponent(entityId)}/attrs`,
|
|
1472
|
+
data
|
|
1473
|
+
);
|
|
1474
|
+
printSuccess("Attributes added.");
|
|
1475
|
+
}
|
|
1476
|
+
)
|
|
1477
|
+
);
|
|
1478
|
+
addExamples(add, [
|
|
1479
|
+
{
|
|
1480
|
+
description: "Add attributes from a file",
|
|
1481
|
+
command: "geonic entities attrs add urn:ngsi-ld:Sensor:001 @attrs.json"
|
|
1482
|
+
}
|
|
1483
|
+
]);
|
|
1484
|
+
const attrUpdate = attrs.command("update").description("Update a specific attribute of an entity").argument("<entityId>", "Entity ID").argument("<attrName>", "Attribute name").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
1485
|
+
withErrorHandler(
|
|
1486
|
+
async (entityId, attrName, json, _opts, cmd) => {
|
|
1487
|
+
const client = createClient(cmd);
|
|
1488
|
+
const data = await parseJsonInput(json);
|
|
1489
|
+
await client.put(
|
|
1490
|
+
`/entities/${encodeURIComponent(entityId)}/attrs/${encodeURIComponent(attrName)}`,
|
|
1491
|
+
data
|
|
1492
|
+
);
|
|
1493
|
+
printSuccess("Attribute updated.");
|
|
1494
|
+
}
|
|
1495
|
+
)
|
|
1496
|
+
);
|
|
1497
|
+
addExamples(attrUpdate, [
|
|
1498
|
+
{
|
|
1499
|
+
description: "Update a specific attribute",
|
|
1500
|
+
command: `geonic entities attrs update urn:ngsi-ld:Sensor:001 temperature '{"value":25}'`
|
|
1501
|
+
}
|
|
1502
|
+
]);
|
|
1503
|
+
const del = attrs.command("delete").description("Delete a specific attribute from an entity").argument("<entityId>", "Entity ID").argument("<attrName>", "Attribute name").action(
|
|
1504
|
+
withErrorHandler(
|
|
1505
|
+
async (entityId, attrName, _opts, cmd) => {
|
|
1506
|
+
const client = createClient(cmd);
|
|
1507
|
+
await client.delete(
|
|
1508
|
+
`/entities/${encodeURIComponent(entityId)}/attrs/${encodeURIComponent(attrName)}`
|
|
1509
|
+
);
|
|
1510
|
+
printSuccess("Attribute deleted.");
|
|
1511
|
+
}
|
|
1512
|
+
)
|
|
1513
|
+
);
|
|
1514
|
+
addExamples(del, [
|
|
1515
|
+
{
|
|
1516
|
+
description: "Delete a specific attribute",
|
|
1517
|
+
command: "geonic entities attrs delete urn:ngsi-ld:Sensor:001 temperature"
|
|
1518
|
+
}
|
|
1519
|
+
]);
|
|
1520
|
+
}
|
|
1521
|
+
function registerAttrsSubcommand(entitiesCmd) {
|
|
1522
|
+
const attrs = entitiesCmd.command("attrs").description("Manage entity attributes");
|
|
1523
|
+
addAttrsSubcommands(attrs);
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
// src/commands/entities.ts
|
|
1527
|
+
function registerEntitiesCommand(program2) {
|
|
1528
|
+
const entities = program2.command("entities").description("Manage context entities");
|
|
1529
|
+
const list = entities.command("list").description("List entities with optional filters").option("--type <type>", "Filter by entity type").option("--id-pattern <pat>", "Filter by entity ID pattern (regex)").option("--query <q>", "NGSI query expression").option("--attrs <a,b>", "Comma-separated list of attributes to include").option("--georel <rel>", "Geo-relationship (e.g. near;maxDistance:1000)").option("--geometry <geo>", "Geometry type for geo-query (e.g. point)").option("--coords <coords>", "Coordinates for geo-query").option("--spatial-id <zfxy>", "Spatial ID filter (ZFXY tile)").option("--limit <n>", "Maximum number of entities to return", parseInt).option("--offset <n>", "Skip first N entities", parseInt).option("--order-by <field>", "Order results by field").option("--count", "Include total count in response").option("--key-values", "Request simplified key-value format").action(
|
|
1530
|
+
withErrorHandler(async (opts, cmd) => {
|
|
1531
|
+
const client = createClient(cmd);
|
|
1532
|
+
const format = getFormat(cmd);
|
|
1533
|
+
const params = {};
|
|
1534
|
+
if (opts.type) params.type = String(opts.type);
|
|
1535
|
+
if (opts.idPattern) params.idPattern = String(opts.idPattern);
|
|
1536
|
+
if (opts.query) params.q = String(opts.query);
|
|
1537
|
+
if (opts.attrs) params.attrs = String(opts.attrs);
|
|
1538
|
+
if (opts.georel) params.georel = String(opts.georel);
|
|
1539
|
+
if (opts.geometry) params.geometry = String(opts.geometry);
|
|
1540
|
+
if (opts.coords) params.coords = String(opts.coords);
|
|
1541
|
+
if (opts.spatialId) params.spatialId = String(opts.spatialId);
|
|
1542
|
+
if (opts.limit !== void 0) params.limit = String(opts.limit);
|
|
1543
|
+
if (opts.offset !== void 0) params.offset = String(opts.offset);
|
|
1544
|
+
if (opts.orderBy) params.orderBy = String(opts.orderBy);
|
|
1545
|
+
if (opts.count) params.options = "count";
|
|
1546
|
+
if (opts.keyValues) {
|
|
1547
|
+
params.options = params.options ? `${params.options},keyValues` : "keyValues";
|
|
1548
|
+
}
|
|
1549
|
+
const response = await client.get("/entities", params);
|
|
1550
|
+
outputResponse(response, format, !!opts.count);
|
|
1551
|
+
})
|
|
1552
|
+
);
|
|
1553
|
+
addExamples(list, [
|
|
1554
|
+
{
|
|
1555
|
+
description: "Filter by entity type",
|
|
1556
|
+
command: "geonic entities list --type Sensor"
|
|
1557
|
+
},
|
|
1558
|
+
{
|
|
1559
|
+
description: "Filter by entity ID pattern (regex)",
|
|
1560
|
+
command: "geonic entities list --id-pattern 'urn:ngsi-ld:Sensor:.*'"
|
|
1561
|
+
},
|
|
1562
|
+
{
|
|
1563
|
+
description: "Filter by attribute value",
|
|
1564
|
+
command: "geonic entities list --query 'temperature>30'"
|
|
1565
|
+
},
|
|
1566
|
+
{
|
|
1567
|
+
description: "AND conditions (semicolon)",
|
|
1568
|
+
command: "geonic entities list --query 'temperature>30;humidity<50'"
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
description: "String match",
|
|
1572
|
+
command: `geonic entities list --query 'name=="Tokyo"'`
|
|
1573
|
+
},
|
|
1574
|
+
{
|
|
1575
|
+
description: "Pattern match",
|
|
1576
|
+
command: `geonic entities list --query 'name~="Tok.*"'`
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
description: "Check attribute existence",
|
|
1580
|
+
command: "geonic entities list --query 'temperature'"
|
|
1581
|
+
},
|
|
1582
|
+
{
|
|
1583
|
+
description: "Select specific attributes",
|
|
1584
|
+
command: "geonic entities list --attrs temperature,humidity"
|
|
1585
|
+
},
|
|
1586
|
+
{
|
|
1587
|
+
description: "Geo-query: entities near a point (within 1km)",
|
|
1588
|
+
command: "geonic entities list --georel 'near;maxDistance==1000' --geometry Point --coords '[139.7671,35.6812]'"
|
|
1589
|
+
},
|
|
1590
|
+
{
|
|
1591
|
+
description: "Geo-query: entities within a polygon",
|
|
1592
|
+
command: "geonic entities list --georel within --geometry Polygon --coords '[[[139.7,35.7],[139.8,35.7],[139.8,35.6],[139.7,35.6],[139.7,35.7]]]'"
|
|
1593
|
+
},
|
|
1594
|
+
{
|
|
1595
|
+
description: "Filter by Spatial ID (ZFXY tile)",
|
|
1596
|
+
command: "geonic entities list --spatial-id 15/0/29101/12903"
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
description: "Paginate results",
|
|
1600
|
+
command: "geonic entities list --limit 20 --offset 40"
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
description: "Order by attribute",
|
|
1604
|
+
command: "geonic entities list --order-by temperature"
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
description: "Get total count with results",
|
|
1608
|
+
command: "geonic entities list --type Sensor --count"
|
|
1609
|
+
}
|
|
1610
|
+
]);
|
|
1611
|
+
const get = entities.command("get").description("Get a single entity by ID").argument("<id>", "Entity ID").option("--key-values", "Request simplified key-value format").action(
|
|
1612
|
+
withErrorHandler(async (id, opts, cmd) => {
|
|
1613
|
+
const client = createClient(cmd);
|
|
1614
|
+
const format = getFormat(cmd);
|
|
1615
|
+
const params = {};
|
|
1616
|
+
if (opts.keyValues) {
|
|
1617
|
+
params.options = "keyValues";
|
|
1618
|
+
}
|
|
1619
|
+
const response = await client.get(
|
|
1620
|
+
`/entities/${encodeURIComponent(id)}`,
|
|
1621
|
+
params
|
|
1622
|
+
);
|
|
1623
|
+
outputResponse(response, format);
|
|
1624
|
+
})
|
|
1625
|
+
);
|
|
1626
|
+
addExamples(get, [
|
|
1627
|
+
{
|
|
1628
|
+
description: "Get entity by ID",
|
|
1629
|
+
command: "geonic entities get urn:ngsi-ld:Sensor:001"
|
|
1630
|
+
},
|
|
1631
|
+
{
|
|
1632
|
+
description: "Get entity in keyValues format",
|
|
1633
|
+
command: "geonic entities get urn:ngsi-ld:Sensor:001 --key-values"
|
|
1634
|
+
}
|
|
1635
|
+
]);
|
|
1636
|
+
const create = entities.command("create").description("Create a new entity").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
1637
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1638
|
+
const client = createClient(cmd);
|
|
1639
|
+
const data = await parseJsonInput(json);
|
|
1640
|
+
await client.post("/entities", data);
|
|
1641
|
+
printSuccess("Entity created.");
|
|
1642
|
+
})
|
|
1643
|
+
);
|
|
1644
|
+
addExamples(create, [
|
|
1645
|
+
{
|
|
1646
|
+
description: "Create from inline JSON",
|
|
1647
|
+
command: `geonic entities create '{"id":"urn:ngsi-ld:Sensor:001","type":"Sensor"}'`
|
|
1648
|
+
},
|
|
1649
|
+
{
|
|
1650
|
+
description: "Create from a file",
|
|
1651
|
+
command: "geonic entities create @entity.json"
|
|
1652
|
+
},
|
|
1653
|
+
{
|
|
1654
|
+
description: "Create from stdin pipe",
|
|
1655
|
+
command: "cat entity.json | geonic entities create"
|
|
1656
|
+
}
|
|
1657
|
+
]);
|
|
1658
|
+
const update = entities.command("update").description("Update attributes of an entity (PATCH)").argument("<id>", "Entity ID").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
1659
|
+
withErrorHandler(
|
|
1660
|
+
async (id, json, _opts, cmd) => {
|
|
1661
|
+
const client = createClient(cmd);
|
|
1662
|
+
const data = await parseJsonInput(json);
|
|
1663
|
+
await client.patch(
|
|
1664
|
+
`/entities/${encodeURIComponent(id)}/attrs`,
|
|
1665
|
+
data
|
|
1666
|
+
);
|
|
1667
|
+
printSuccess("Entity updated.");
|
|
1668
|
+
}
|
|
1669
|
+
)
|
|
1670
|
+
);
|
|
1671
|
+
addExamples(update, [
|
|
1672
|
+
{
|
|
1673
|
+
description: "Update entity attributes from inline JSON",
|
|
1674
|
+
command: `geonic entities update urn:ngsi-ld:Sensor:001 '{"temperature":{"value":25}}'`
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
description: "Update entity attributes from a file",
|
|
1678
|
+
command: "geonic entities update urn:ngsi-ld:Sensor:001 @attrs.json"
|
|
1679
|
+
}
|
|
1680
|
+
]);
|
|
1681
|
+
const replace = entities.command("replace").description("Replace all attributes of an entity (PUT)").argument("<id>", "Entity ID").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
1682
|
+
withErrorHandler(
|
|
1683
|
+
async (id, json, _opts, cmd) => {
|
|
1684
|
+
const client = createClient(cmd);
|
|
1685
|
+
const data = await parseJsonInput(json);
|
|
1686
|
+
await client.put(
|
|
1687
|
+
`/entities/${encodeURIComponent(id)}/attrs`,
|
|
1688
|
+
data
|
|
1689
|
+
);
|
|
1690
|
+
printSuccess("Entity replaced.");
|
|
1691
|
+
}
|
|
1692
|
+
)
|
|
1693
|
+
);
|
|
1694
|
+
addExamples(replace, [
|
|
1695
|
+
{
|
|
1696
|
+
description: "Replace all attributes from a file",
|
|
1697
|
+
command: "geonic entities replace urn:ngsi-ld:Sensor:001 @attrs.json"
|
|
1698
|
+
}
|
|
1699
|
+
]);
|
|
1700
|
+
const upsert = entities.command("upsert").description("Create or update entities").argument("[json]", "JSON payload (inline, @file, - for stdin, or omit for interactive/pipe)").action(
|
|
1701
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1702
|
+
const client = createClient(cmd);
|
|
1703
|
+
const data = await parseJsonInput(json);
|
|
1704
|
+
await client.post("/entityOperations/upsert", data);
|
|
1705
|
+
printSuccess("Entity upserted.");
|
|
1706
|
+
})
|
|
1707
|
+
);
|
|
1708
|
+
addExamples(upsert, [
|
|
1709
|
+
{
|
|
1710
|
+
description: "Upsert entities from a file",
|
|
1711
|
+
command: "geonic entities upsert @entities.json"
|
|
1712
|
+
},
|
|
1713
|
+
{
|
|
1714
|
+
description: "Upsert from stdin pipe",
|
|
1715
|
+
command: "cat entities.json | geonic entities upsert"
|
|
1716
|
+
}
|
|
1717
|
+
]);
|
|
1718
|
+
const del = entities.command("delete").description("Delete an entity by ID").argument("<id>", "Entity ID").action(
|
|
1719
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
1720
|
+
const client = createClient(cmd);
|
|
1721
|
+
await client.delete(`/entities/${encodeURIComponent(id)}`);
|
|
1722
|
+
printSuccess("Entity deleted.");
|
|
1723
|
+
})
|
|
1724
|
+
);
|
|
1725
|
+
addExamples(del, [
|
|
1726
|
+
{
|
|
1727
|
+
description: "Delete an entity by ID",
|
|
1728
|
+
command: "geonic entities delete urn:ngsi-ld:Sensor:001"
|
|
1729
|
+
}
|
|
1730
|
+
]);
|
|
1731
|
+
registerAttrsSubcommand(entities);
|
|
1732
|
+
}
|
|
1733
|
+
|
|
1734
|
+
// src/commands/batch.ts
|
|
1735
|
+
function registerBatchCommand(program2) {
|
|
1736
|
+
const batch = program2.command("entityOperations").alias("batch").description("Perform batch operations on entities");
|
|
1737
|
+
const create = batch.command("create [json]").description("Batch create entities").action(
|
|
1738
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1739
|
+
const client = createClient(cmd);
|
|
1740
|
+
const format = getFormat(cmd);
|
|
1741
|
+
const data = await parseJsonInput(json);
|
|
1742
|
+
const response = await client.post("/entityOperations/create", data);
|
|
1743
|
+
outputResponse(response, format);
|
|
1744
|
+
})
|
|
1745
|
+
);
|
|
1746
|
+
addExamples(create, [
|
|
1747
|
+
{
|
|
1748
|
+
description: "Batch create from a file",
|
|
1749
|
+
command: "geonic batch create @entities.json"
|
|
1750
|
+
},
|
|
1751
|
+
{
|
|
1752
|
+
description: "Batch create from stdin",
|
|
1753
|
+
command: "cat entities.json | geonic batch create"
|
|
1754
|
+
}
|
|
1755
|
+
]);
|
|
1756
|
+
const upsert = batch.command("upsert [json]").description("Batch upsert entities").action(
|
|
1757
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1758
|
+
const client = createClient(cmd);
|
|
1759
|
+
const format = getFormat(cmd);
|
|
1760
|
+
const data = await parseJsonInput(json);
|
|
1761
|
+
const response = await client.post("/entityOperations/upsert", data);
|
|
1762
|
+
outputResponse(response, format);
|
|
1763
|
+
})
|
|
1764
|
+
);
|
|
1765
|
+
addExamples(upsert, [
|
|
1766
|
+
{
|
|
1767
|
+
description: "Batch upsert from a file",
|
|
1768
|
+
command: "geonic batch upsert @entities.json"
|
|
1769
|
+
},
|
|
1770
|
+
{
|
|
1771
|
+
description: "Batch upsert from stdin",
|
|
1772
|
+
command: "cat entities.json | geonic batch upsert"
|
|
1773
|
+
}
|
|
1774
|
+
]);
|
|
1775
|
+
const update = batch.command("update [json]").description("Batch update entity attributes").action(
|
|
1776
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1777
|
+
const client = createClient(cmd);
|
|
1778
|
+
const format = getFormat(cmd);
|
|
1779
|
+
const data = await parseJsonInput(json);
|
|
1780
|
+
const response = await client.post("/entityOperations/update", data);
|
|
1781
|
+
outputResponse(response, format);
|
|
1782
|
+
})
|
|
1783
|
+
);
|
|
1784
|
+
addExamples(update, [
|
|
1785
|
+
{
|
|
1786
|
+
description: "Batch update from a file",
|
|
1787
|
+
command: "geonic batch update @updates.json"
|
|
1788
|
+
},
|
|
1789
|
+
{
|
|
1790
|
+
description: "Batch update from stdin",
|
|
1791
|
+
command: "cat updates.json | geonic batch update"
|
|
1792
|
+
}
|
|
1793
|
+
]);
|
|
1794
|
+
const del = batch.command("delete [json]").description("Batch delete entities by ID").action(
|
|
1795
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1796
|
+
const client = createClient(cmd);
|
|
1797
|
+
const format = getFormat(cmd);
|
|
1798
|
+
const data = await parseJsonInput(json);
|
|
1799
|
+
const response = await client.post("/entityOperations/delete", data);
|
|
1800
|
+
outputResponse(response, format);
|
|
1801
|
+
})
|
|
1802
|
+
);
|
|
1803
|
+
addExamples(del, [
|
|
1804
|
+
{
|
|
1805
|
+
description: "Batch delete from a file",
|
|
1806
|
+
command: "geonic batch delete @entity-ids.json"
|
|
1807
|
+
},
|
|
1808
|
+
{
|
|
1809
|
+
description: "Batch delete from stdin",
|
|
1810
|
+
command: "cat entity-ids.json | geonic batch delete"
|
|
1811
|
+
}
|
|
1812
|
+
]);
|
|
1813
|
+
const query = batch.command("query [json]").description("Query entities by posting a query payload").action(
|
|
1814
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1815
|
+
const client = createClient(cmd);
|
|
1816
|
+
const format = getFormat(cmd);
|
|
1817
|
+
const data = await parseJsonInput(json);
|
|
1818
|
+
const response = await client.post("/entityOperations/query", data);
|
|
1819
|
+
outputResponse(response, format);
|
|
1820
|
+
})
|
|
1821
|
+
);
|
|
1822
|
+
addExamples(query, [
|
|
1823
|
+
{
|
|
1824
|
+
description: "Query entities from a file",
|
|
1825
|
+
command: "geonic batch query @query.json"
|
|
1826
|
+
},
|
|
1827
|
+
{
|
|
1828
|
+
description: "Query entities from stdin",
|
|
1829
|
+
command: "cat query.json | geonic batch query"
|
|
1830
|
+
}
|
|
1831
|
+
]);
|
|
1832
|
+
const merge = batch.command("merge [json]").description("Batch merge-patch entities").action(
|
|
1833
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1834
|
+
const client = createClient(cmd);
|
|
1835
|
+
const format = getFormat(cmd);
|
|
1836
|
+
const data = await parseJsonInput(json);
|
|
1837
|
+
const response = await client.post("/entityOperations/merge", data);
|
|
1838
|
+
outputResponse(response, format);
|
|
1839
|
+
})
|
|
1840
|
+
);
|
|
1841
|
+
addExamples(merge, [
|
|
1842
|
+
{
|
|
1843
|
+
description: "Batch merge-patch from a file",
|
|
1844
|
+
command: "geonic batch merge @patches.json"
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
description: "Batch merge-patch from stdin",
|
|
1848
|
+
command: "cat patches.json | geonic batch merge"
|
|
1849
|
+
}
|
|
1850
|
+
]);
|
|
1851
|
+
}
|
|
1852
|
+
|
|
1853
|
+
// src/commands/subscriptions.ts
|
|
1854
|
+
function registerSubscriptionsCommand(program2) {
|
|
1855
|
+
const subscriptions = program2.command("subscriptions").alias("sub").description("Manage context subscriptions");
|
|
1856
|
+
const list = subscriptions.command("list").description("List subscriptions").option("--limit <n>", "Maximum number of results", parseInt).option("--offset <n>", "Skip N results", parseInt).option("--count", "Include total count in response").action(
|
|
1857
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
1858
|
+
const client = createClient(cmd);
|
|
1859
|
+
const format = getFormat(cmd);
|
|
1860
|
+
const cmdOpts = cmd.opts();
|
|
1861
|
+
const params = {};
|
|
1862
|
+
if (cmdOpts.limit !== void 0) params["limit"] = String(cmdOpts.limit);
|
|
1863
|
+
if (cmdOpts.offset !== void 0) params["offset"] = String(cmdOpts.offset);
|
|
1864
|
+
if (cmdOpts.count) params["options"] = "count";
|
|
1865
|
+
const response = await client.get("/subscriptions", params);
|
|
1866
|
+
outputResponse(response, format, !!cmdOpts.count);
|
|
1867
|
+
})
|
|
1868
|
+
);
|
|
1869
|
+
addExamples(list, [
|
|
1870
|
+
{
|
|
1871
|
+
description: "List all subscriptions",
|
|
1872
|
+
command: "geonic subscriptions list"
|
|
1873
|
+
},
|
|
1874
|
+
{
|
|
1875
|
+
description: "List with pagination",
|
|
1876
|
+
command: "geonic subscriptions list --limit 10 --offset 20"
|
|
1877
|
+
},
|
|
1878
|
+
{
|
|
1879
|
+
description: "List with total count",
|
|
1880
|
+
command: "geonic subscriptions list --count"
|
|
1881
|
+
}
|
|
1882
|
+
]);
|
|
1883
|
+
const get = subscriptions.command("get <id>").description("Get a subscription by ID").action(
|
|
1884
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
1885
|
+
const client = createClient(cmd);
|
|
1886
|
+
const format = getFormat(cmd);
|
|
1887
|
+
const response = await client.get(
|
|
1888
|
+
`/subscriptions/${encodeURIComponent(String(id))}`
|
|
1889
|
+
);
|
|
1890
|
+
outputResponse(response, format);
|
|
1891
|
+
})
|
|
1892
|
+
);
|
|
1893
|
+
addExamples(get, [
|
|
1894
|
+
{
|
|
1895
|
+
description: "Get subscription by ID",
|
|
1896
|
+
command: "geonic subscriptions get urn:ngsi-ld:Subscription:001"
|
|
1897
|
+
}
|
|
1898
|
+
]);
|
|
1899
|
+
const create = subscriptions.command("create [json]").description("Create a subscription").action(
|
|
1900
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
1901
|
+
const client = createClient(cmd);
|
|
1902
|
+
const format = getFormat(cmd);
|
|
1903
|
+
const data = await parseJsonInput(json);
|
|
1904
|
+
const response = await client.post("/subscriptions", data);
|
|
1905
|
+
outputResponse(response, format);
|
|
1906
|
+
printSuccess("Subscription created.");
|
|
1907
|
+
})
|
|
1908
|
+
);
|
|
1909
|
+
addExamples(create, [
|
|
1910
|
+
{
|
|
1911
|
+
description: "Create from a JSON file",
|
|
1912
|
+
command: "geonic subscriptions create @subscription.json"
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
description: "Create from stdin",
|
|
1916
|
+
command: "cat subscription.json | geonic subscriptions create"
|
|
1917
|
+
}
|
|
1918
|
+
]);
|
|
1919
|
+
const update = subscriptions.command("update <id> [json]").description("Update a subscription").action(
|
|
1920
|
+
withErrorHandler(
|
|
1921
|
+
async (id, json, _opts, cmd) => {
|
|
1922
|
+
const client = createClient(cmd);
|
|
1923
|
+
const format = getFormat(cmd);
|
|
1924
|
+
const data = await parseJsonInput(json);
|
|
1925
|
+
const response = await client.patch(
|
|
1926
|
+
`/subscriptions/${encodeURIComponent(String(id))}`,
|
|
1927
|
+
data
|
|
1928
|
+
);
|
|
1929
|
+
outputResponse(response, format);
|
|
1930
|
+
printSuccess("Subscription updated.");
|
|
1931
|
+
}
|
|
1932
|
+
)
|
|
1933
|
+
);
|
|
1934
|
+
addExamples(update, [
|
|
1935
|
+
{
|
|
1936
|
+
description: "Update a subscription from a file",
|
|
1937
|
+
command: "geonic subscriptions update urn:ngsi-ld:Subscription:001 @sub.json"
|
|
1938
|
+
}
|
|
1939
|
+
]);
|
|
1940
|
+
const del = subscriptions.command("delete <id>").description("Delete a subscription").action(
|
|
1941
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
1942
|
+
const client = createClient(cmd);
|
|
1943
|
+
await client.delete(
|
|
1944
|
+
`/subscriptions/${encodeURIComponent(String(id))}`
|
|
1945
|
+
);
|
|
1946
|
+
printSuccess("Subscription deleted.");
|
|
1947
|
+
})
|
|
1948
|
+
);
|
|
1949
|
+
addExamples(del, [
|
|
1950
|
+
{
|
|
1951
|
+
description: "Delete a subscription",
|
|
1952
|
+
command: "geonic subscriptions delete urn:ngsi-ld:Subscription:001"
|
|
1953
|
+
}
|
|
1954
|
+
]);
|
|
1955
|
+
}
|
|
1956
|
+
|
|
1957
|
+
// src/commands/registrations.ts
|
|
1958
|
+
function registerRegistrationsCommand(program2) {
|
|
1959
|
+
const registrations = program2.command("registrations").alias("reg").description("Manage context registrations");
|
|
1960
|
+
const list = registrations.command("list").description("List registrations").option("--limit <n>", "Maximum number of results", parseInt).option("--offset <n>", "Skip N results", parseInt).option("--count", "Include total count in response").action(
|
|
1961
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
1962
|
+
const client = createClient(cmd);
|
|
1963
|
+
const format = getFormat(cmd);
|
|
1964
|
+
const cmdOpts = cmd.opts();
|
|
1965
|
+
const params = {};
|
|
1966
|
+
if (cmdOpts.limit !== void 0) params["limit"] = String(cmdOpts.limit);
|
|
1967
|
+
if (cmdOpts.offset !== void 0) params["offset"] = String(cmdOpts.offset);
|
|
1968
|
+
if (cmdOpts.count) params["options"] = "count";
|
|
1969
|
+
const response = await client.get("/registrations", params);
|
|
1970
|
+
outputResponse(response, format, !!cmdOpts.count);
|
|
1971
|
+
})
|
|
1972
|
+
);
|
|
1973
|
+
addExamples(list, [
|
|
1974
|
+
{
|
|
1975
|
+
description: "List all registrations",
|
|
1976
|
+
command: "geonic registrations list"
|
|
1977
|
+
},
|
|
1978
|
+
{
|
|
1979
|
+
description: "List with pagination",
|
|
1980
|
+
command: "geonic registrations list --limit 10"
|
|
1981
|
+
}
|
|
1982
|
+
]);
|
|
1983
|
+
const get = registrations.command("get <id>").description("Get a registration by ID").action(
|
|
1984
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
1985
|
+
const client = createClient(cmd);
|
|
1986
|
+
const format = getFormat(cmd);
|
|
1987
|
+
const response = await client.get(
|
|
1988
|
+
`/registrations/${encodeURIComponent(String(id))}`
|
|
1989
|
+
);
|
|
1990
|
+
outputResponse(response, format);
|
|
1991
|
+
})
|
|
1992
|
+
);
|
|
1993
|
+
addExamples(get, [
|
|
1994
|
+
{
|
|
1995
|
+
description: "Get registration by ID",
|
|
1996
|
+
command: "geonic registrations get urn:ngsi-ld:ContextSourceRegistration:001"
|
|
1997
|
+
}
|
|
1998
|
+
]);
|
|
1999
|
+
const create = registrations.command("create [json]").description("Create a registration").action(
|
|
2000
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2001
|
+
const client = createClient(cmd);
|
|
2002
|
+
const format = getFormat(cmd);
|
|
2003
|
+
const data = await parseJsonInput(json);
|
|
2004
|
+
const response = await client.post("/registrations", data);
|
|
2005
|
+
outputResponse(response, format);
|
|
2006
|
+
printSuccess("Registration created.");
|
|
2007
|
+
})
|
|
2008
|
+
);
|
|
2009
|
+
addExamples(create, [
|
|
2010
|
+
{
|
|
2011
|
+
description: "Create a registration from a file",
|
|
2012
|
+
command: "geonic registrations create @registration.json"
|
|
2013
|
+
}
|
|
2014
|
+
]);
|
|
2015
|
+
const regUpdate = registrations.command("update <id> [json]").description("Update a registration").action(
|
|
2016
|
+
withErrorHandler(
|
|
2017
|
+
async (id, json, _opts, cmd) => {
|
|
2018
|
+
const client = createClient(cmd);
|
|
2019
|
+
const format = getFormat(cmd);
|
|
2020
|
+
const data = await parseJsonInput(json);
|
|
2021
|
+
const response = await client.patch(
|
|
2022
|
+
`/registrations/${encodeURIComponent(String(id))}`,
|
|
2023
|
+
data
|
|
2024
|
+
);
|
|
2025
|
+
outputResponse(response, format);
|
|
2026
|
+
printSuccess("Registration updated.");
|
|
2027
|
+
}
|
|
2028
|
+
)
|
|
2029
|
+
);
|
|
2030
|
+
addExamples(regUpdate, [
|
|
2031
|
+
{
|
|
2032
|
+
description: "Update a registration from a file",
|
|
2033
|
+
command: "geonic registrations update urn:ngsi-ld:ContextSourceRegistration:001 @registration.json"
|
|
2034
|
+
}
|
|
2035
|
+
]);
|
|
2036
|
+
const del = registrations.command("delete <id>").description("Delete a registration").action(
|
|
2037
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2038
|
+
const client = createClient(cmd);
|
|
2039
|
+
await client.delete(
|
|
2040
|
+
`/registrations/${encodeURIComponent(String(id))}`
|
|
2041
|
+
);
|
|
2042
|
+
printSuccess("Registration deleted.");
|
|
2043
|
+
})
|
|
2044
|
+
);
|
|
2045
|
+
addExamples(del, [
|
|
2046
|
+
{
|
|
2047
|
+
description: "Delete a registration",
|
|
2048
|
+
command: "geonic registrations delete urn:ngsi-ld:ContextSourceRegistration:001"
|
|
2049
|
+
}
|
|
2050
|
+
]);
|
|
2051
|
+
}
|
|
2052
|
+
|
|
2053
|
+
// src/commands/types.ts
|
|
2054
|
+
function registerTypesCommand(program2) {
|
|
2055
|
+
const types = program2.command("types").description("Browse entity types");
|
|
2056
|
+
const list = types.command("list").description("List available entity types").action(
|
|
2057
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2058
|
+
const client = createClient(cmd);
|
|
2059
|
+
const format = getFormat(cmd);
|
|
2060
|
+
const response = await client.get("/types");
|
|
2061
|
+
outputResponse(response, format);
|
|
2062
|
+
})
|
|
2063
|
+
);
|
|
2064
|
+
addExamples(list, [
|
|
2065
|
+
{
|
|
2066
|
+
description: "List all entity types",
|
|
2067
|
+
command: "geonic types list"
|
|
2068
|
+
}
|
|
2069
|
+
]);
|
|
2070
|
+
const get = types.command("get <typeName>").description("Get details for an entity type").action(
|
|
2071
|
+
withErrorHandler(
|
|
2072
|
+
async (typeName, _opts, cmd) => {
|
|
2073
|
+
const client = createClient(cmd);
|
|
2074
|
+
const format = getFormat(cmd);
|
|
2075
|
+
const response = await client.get(
|
|
2076
|
+
`/types/${encodeURIComponent(String(typeName))}`
|
|
2077
|
+
);
|
|
2078
|
+
outputResponse(response, format);
|
|
2079
|
+
}
|
|
2080
|
+
)
|
|
2081
|
+
);
|
|
2082
|
+
addExamples(get, [
|
|
2083
|
+
{
|
|
2084
|
+
description: "Get details for a specific type",
|
|
2085
|
+
command: "geonic types get Sensor"
|
|
2086
|
+
}
|
|
2087
|
+
]);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2090
|
+
// src/commands/temporal.ts
|
|
2091
|
+
function addTemporalListOptions(cmd) {
|
|
2092
|
+
return cmd.option("--type <type>", "Filter by entity type").option("--attrs <a,b>", "Comma-separated list of attributes to include").option("--query <q>", "NGSI query expression").option("--georel <rel>", "Geo-relationship (e.g. near;maxDistance:1000)").option("--geometry <geo>", "Geometry type for geo-query (e.g. point)").option("--coords <coords>", "Coordinates for geo-query").option("--time-rel <rel>", "Temporal relationship (before, after, between)").option("--time-at <time>", "Temporal query start time (ISO 8601)").option("--end-time-at <time>", "Temporal query end time (ISO 8601)").option("--last-n <n>", "Return last N temporal values", parseInt).option("--limit <n>", "Maximum number of entities to return", parseInt).option("--offset <n>", "Skip first N entities", parseInt).option("--count", "Include total count in response");
|
|
2093
|
+
}
|
|
2094
|
+
function createListAction() {
|
|
2095
|
+
return withErrorHandler(async (_opts, cmd) => {
|
|
2096
|
+
const client = createClient(cmd);
|
|
2097
|
+
const format = getFormat(cmd);
|
|
2098
|
+
const cmdOpts = cmd.opts();
|
|
2099
|
+
const params = {};
|
|
2100
|
+
if (cmdOpts.type) params["type"] = cmdOpts.type;
|
|
2101
|
+
if (cmdOpts.attrs) params["attrs"] = cmdOpts.attrs;
|
|
2102
|
+
if (cmdOpts.query) params["q"] = cmdOpts.query;
|
|
2103
|
+
if (cmdOpts.georel) params["georel"] = cmdOpts.georel;
|
|
2104
|
+
if (cmdOpts.geometry) params["geometry"] = cmdOpts.geometry;
|
|
2105
|
+
if (cmdOpts.coords) params["coords"] = cmdOpts.coords;
|
|
2106
|
+
if (cmdOpts.timeRel) params["timerel"] = cmdOpts.timeRel;
|
|
2107
|
+
if (cmdOpts.timeAt) params["timeAt"] = cmdOpts.timeAt;
|
|
2108
|
+
if (cmdOpts.endTimeAt) params["endTimeAt"] = cmdOpts.endTimeAt;
|
|
2109
|
+
if (cmdOpts.lastN !== void 0) params["lastN"] = String(cmdOpts.lastN);
|
|
2110
|
+
if (cmdOpts.limit !== void 0) params["limit"] = String(cmdOpts.limit);
|
|
2111
|
+
if (cmdOpts.offset !== void 0) params["offset"] = String(cmdOpts.offset);
|
|
2112
|
+
if (cmdOpts.count) params["options"] = "count";
|
|
2113
|
+
const response = await client.get("/temporal/entities", params);
|
|
2114
|
+
outputResponse(response, format, cmdOpts.count);
|
|
2115
|
+
});
|
|
2116
|
+
}
|
|
2117
|
+
function addTemporalGetOptions(cmd) {
|
|
2118
|
+
return cmd.option("--attrs <a,b>", "Comma-separated list of attributes to include").option("--time-rel <rel>", "Temporal relationship (before, after, between)").option("--time-at <time>", "Temporal query start time (ISO 8601)").option("--end-time-at <time>", "Temporal query end time (ISO 8601)").option("--last-n <n>", "Return last N temporal values", parseInt);
|
|
2119
|
+
}
|
|
2120
|
+
function createGetAction() {
|
|
2121
|
+
return withErrorHandler(async (id, _opts, cmd) => {
|
|
2122
|
+
const client = createClient(cmd);
|
|
2123
|
+
const format = getFormat(cmd);
|
|
2124
|
+
const cmdOpts = cmd.opts();
|
|
2125
|
+
const params = {};
|
|
2126
|
+
if (cmdOpts.attrs) params["attrs"] = cmdOpts.attrs;
|
|
2127
|
+
if (cmdOpts.timeRel) params["timerel"] = cmdOpts.timeRel;
|
|
2128
|
+
if (cmdOpts.timeAt) params["timeAt"] = cmdOpts.timeAt;
|
|
2129
|
+
if (cmdOpts.endTimeAt) params["endTimeAt"] = cmdOpts.endTimeAt;
|
|
2130
|
+
if (cmdOpts.lastN !== void 0) params["lastN"] = String(cmdOpts.lastN);
|
|
2131
|
+
const response = await client.get(
|
|
2132
|
+
`/temporal/entities/${encodeURIComponent(String(id))}`,
|
|
2133
|
+
params
|
|
2134
|
+
);
|
|
2135
|
+
outputResponse(response, format);
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
function createCreateAction() {
|
|
2139
|
+
return withErrorHandler(async (json, _opts, cmd) => {
|
|
2140
|
+
const body = await parseJsonInput(json);
|
|
2141
|
+
const client = createClient(cmd);
|
|
2142
|
+
await client.post("/temporal/entities", body);
|
|
2143
|
+
printSuccess("Temporal entity created.");
|
|
2144
|
+
});
|
|
2145
|
+
}
|
|
2146
|
+
function createDeleteAction() {
|
|
2147
|
+
return withErrorHandler(async (id, _opts, cmd) => {
|
|
2148
|
+
const client = createClient(cmd);
|
|
2149
|
+
await client.delete(
|
|
2150
|
+
`/temporal/entities/${encodeURIComponent(String(id))}`
|
|
2151
|
+
);
|
|
2152
|
+
printSuccess("Temporal entity deleted.");
|
|
2153
|
+
});
|
|
2154
|
+
}
|
|
2155
|
+
function createQueryAction() {
|
|
2156
|
+
return withErrorHandler(async (json, _opts, cmd) => {
|
|
2157
|
+
const body = await parseJsonInput(json);
|
|
2158
|
+
const client = createClient(cmd);
|
|
2159
|
+
const format = getFormat(cmd);
|
|
2160
|
+
const cmdOpts = cmd.opts();
|
|
2161
|
+
const params = {};
|
|
2162
|
+
if (cmdOpts.aggrMethods) params["aggrMethods"] = cmdOpts.aggrMethods;
|
|
2163
|
+
if (cmdOpts.aggrPeriod) params["aggrPeriodDuration"] = cmdOpts.aggrPeriod;
|
|
2164
|
+
const response = await client.post(
|
|
2165
|
+
"/temporal/entityOperations/query",
|
|
2166
|
+
body,
|
|
2167
|
+
params
|
|
2168
|
+
);
|
|
2169
|
+
outputResponse(response, format);
|
|
2170
|
+
});
|
|
2171
|
+
}
|
|
2172
|
+
function addQueryOptions(cmd) {
|
|
2173
|
+
return cmd.option("--aggr-methods <methods>", "Aggregation methods (e.g. totalCount,sum)").option("--aggr-period <period>", "Aggregation period (e.g. PT1H)");
|
|
2174
|
+
}
|
|
2175
|
+
function registerTemporalCommand(program2) {
|
|
2176
|
+
const temporal = program2.command("temporal").description("Manage temporal entities");
|
|
2177
|
+
const entities = temporal.command("entities").description("List, get, create, and delete temporal entities");
|
|
2178
|
+
const entityOperations = temporal.command("entityOperations").description("Perform batch operations on temporal entities");
|
|
2179
|
+
const entitiesList = addTemporalListOptions(
|
|
2180
|
+
entities.command("list").description("List temporal entities with optional filters")
|
|
2181
|
+
);
|
|
2182
|
+
entitiesList.action(createListAction());
|
|
2183
|
+
addExamples(entitiesList, [
|
|
2184
|
+
{
|
|
2185
|
+
description: "List by type with time range",
|
|
2186
|
+
command: "geonic temporal entities list --type Sensor --time-rel between --time-at 2025-01-01T00:00:00Z --end-time-at 2025-01-31T23:59:59Z"
|
|
2187
|
+
},
|
|
2188
|
+
{
|
|
2189
|
+
description: "Get last 5 temporal values",
|
|
2190
|
+
command: "geonic temporal entities list --type Sensor --last-n 5"
|
|
2191
|
+
},
|
|
2192
|
+
{
|
|
2193
|
+
description: "Filter by time (after a point)",
|
|
2194
|
+
command: "geonic temporal entities list --time-rel after --time-at 2025-06-01T00:00:00Z"
|
|
2195
|
+
}
|
|
2196
|
+
]);
|
|
2197
|
+
const entitiesGet = addTemporalGetOptions(
|
|
2198
|
+
entities.command("get <id>").description("Get a temporal entity by ID")
|
|
2199
|
+
);
|
|
2200
|
+
entitiesGet.action(createGetAction());
|
|
2201
|
+
addExamples(entitiesGet, [
|
|
2202
|
+
{
|
|
2203
|
+
description: "Get temporal entity with specific attributes",
|
|
2204
|
+
command: "geonic temporal entities get urn:ngsi-ld:Sensor:001 --attrs temperature,humidity"
|
|
2205
|
+
},
|
|
2206
|
+
{
|
|
2207
|
+
description: "Get last 10 values for an entity",
|
|
2208
|
+
command: "geonic temporal entities get urn:ngsi-ld:Sensor:001 --last-n 10"
|
|
2209
|
+
}
|
|
2210
|
+
]);
|
|
2211
|
+
const create = entities.command("create [json]").description("Create a temporal entity").action(createCreateAction());
|
|
2212
|
+
addExamples(create, [
|
|
2213
|
+
{
|
|
2214
|
+
description: "Create temporal entity from a file",
|
|
2215
|
+
command: "geonic temporal entities create @temporal-entity.json"
|
|
2216
|
+
}
|
|
2217
|
+
]);
|
|
2218
|
+
const del = entities.command("delete <id>").description("Delete a temporal entity by ID").action(createDeleteAction());
|
|
2219
|
+
addExamples(del, [
|
|
2220
|
+
{
|
|
2221
|
+
description: "Delete temporal data for an entity",
|
|
2222
|
+
command: "geonic temporal entities delete urn:ngsi-ld:Sensor:001"
|
|
2223
|
+
}
|
|
2224
|
+
]);
|
|
2225
|
+
const opsQuery = addQueryOptions(
|
|
2226
|
+
entityOperations.command("query [json]").description("Query temporal entities (POST)")
|
|
2227
|
+
);
|
|
2228
|
+
opsQuery.action(createQueryAction());
|
|
2229
|
+
addExamples(opsQuery, [
|
|
2230
|
+
{
|
|
2231
|
+
description: "Query with aggregation (hourly count)",
|
|
2232
|
+
command: "geonic temporal entityOperations query @query.json --aggr-methods totalCount --aggr-period PT1H"
|
|
2233
|
+
},
|
|
2234
|
+
{
|
|
2235
|
+
description: "Query from a file",
|
|
2236
|
+
command: "geonic temporal entityOperations query @query.json"
|
|
2237
|
+
}
|
|
2238
|
+
]);
|
|
2239
|
+
addTemporalListOptions(
|
|
2240
|
+
temporal.command("list", { hidden: true }).description("List temporal entities (deprecated: use temporal entities list)")
|
|
2241
|
+
).action(createListAction());
|
|
2242
|
+
addTemporalGetOptions(
|
|
2243
|
+
temporal.command("get <id>", { hidden: true }).description("Get a temporal entity (deprecated: use temporal entities get)")
|
|
2244
|
+
).action(createGetAction());
|
|
2245
|
+
temporal.command("create [json]", { hidden: true }).description("Create a temporal entity (deprecated: use temporal entities create)").action(createCreateAction());
|
|
2246
|
+
temporal.command("delete <id>", { hidden: true }).description("Delete a temporal entity (deprecated: use temporal entities delete)").action(createDeleteAction());
|
|
2247
|
+
addQueryOptions(
|
|
2248
|
+
temporal.command("query [json]", { hidden: true }).description("Query temporal entities (deprecated: use temporal entityOperations query)")
|
|
2249
|
+
).action(createQueryAction());
|
|
2250
|
+
}
|
|
2251
|
+
|
|
2252
|
+
// src/commands/snapshots.ts
|
|
2253
|
+
function registerSnapshotsCommand(program2) {
|
|
2254
|
+
const snapshots = program2.command("snapshots").description("Manage snapshots");
|
|
2255
|
+
const list = snapshots.command("list").description("List snapshots").option("--limit <n>", "Maximum number of snapshots to return", parseInt).option("--offset <n>", "Skip first N snapshots", parseInt).action(
|
|
2256
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2257
|
+
const client = createClient(cmd);
|
|
2258
|
+
const format = getFormat(cmd);
|
|
2259
|
+
const cmdOpts = cmd.opts();
|
|
2260
|
+
const params = {};
|
|
2261
|
+
if (cmdOpts.limit !== void 0) params["limit"] = String(cmdOpts.limit);
|
|
2262
|
+
if (cmdOpts.offset !== void 0) params["offset"] = String(cmdOpts.offset);
|
|
2263
|
+
const response = await client.get("/snapshots", params);
|
|
2264
|
+
outputResponse(response, format);
|
|
2265
|
+
})
|
|
2266
|
+
);
|
|
2267
|
+
addExamples(list, [
|
|
2268
|
+
{
|
|
2269
|
+
description: "List all snapshots",
|
|
2270
|
+
command: "geonic snapshots list"
|
|
2271
|
+
},
|
|
2272
|
+
{
|
|
2273
|
+
description: "List with a limit",
|
|
2274
|
+
command: "geonic snapshots list --limit 10"
|
|
2275
|
+
}
|
|
2276
|
+
]);
|
|
2277
|
+
const get = snapshots.command("get <id>").description("Get a snapshot by ID").action(
|
|
2278
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2279
|
+
const client = createClient(cmd);
|
|
2280
|
+
const format = getFormat(cmd);
|
|
2281
|
+
const response = await client.get(
|
|
2282
|
+
`/snapshots/${encodeURIComponent(String(id))}`
|
|
2283
|
+
);
|
|
2284
|
+
outputResponse(response, format);
|
|
2285
|
+
})
|
|
2286
|
+
);
|
|
2287
|
+
addExamples(get, [
|
|
2288
|
+
{
|
|
2289
|
+
description: "Get a specific snapshot",
|
|
2290
|
+
command: "geonic snapshots get <snapshot-id>"
|
|
2291
|
+
}
|
|
2292
|
+
]);
|
|
2293
|
+
const create = snapshots.command("create").description("Create a new snapshot").action(
|
|
2294
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2295
|
+
const client = createClient(cmd);
|
|
2296
|
+
await client.post("/snapshots");
|
|
2297
|
+
printSuccess("Snapshot created.");
|
|
2298
|
+
})
|
|
2299
|
+
);
|
|
2300
|
+
addExamples(create, [
|
|
2301
|
+
{
|
|
2302
|
+
description: "Create a new snapshot",
|
|
2303
|
+
command: "geonic snapshots create"
|
|
2304
|
+
}
|
|
2305
|
+
]);
|
|
2306
|
+
const del = snapshots.command("delete <id>").description("Delete a snapshot by ID").action(
|
|
2307
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2308
|
+
const client = createClient(cmd);
|
|
2309
|
+
await client.delete(
|
|
2310
|
+
`/snapshots/${encodeURIComponent(String(id))}`
|
|
2311
|
+
);
|
|
2312
|
+
printSuccess("Snapshot deleted.");
|
|
2313
|
+
})
|
|
2314
|
+
);
|
|
2315
|
+
addExamples(del, [
|
|
2316
|
+
{
|
|
2317
|
+
description: "Delete a snapshot",
|
|
2318
|
+
command: "geonic snapshots delete <snapshot-id>"
|
|
2319
|
+
}
|
|
2320
|
+
]);
|
|
2321
|
+
const clone = snapshots.command("clone <id>").description("Clone a snapshot by ID").action(
|
|
2322
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2323
|
+
const client = createClient(cmd);
|
|
2324
|
+
const format = getFormat(cmd);
|
|
2325
|
+
const response = await client.post(
|
|
2326
|
+
`/snapshots/${encodeURIComponent(String(id))}/clone`
|
|
2327
|
+
);
|
|
2328
|
+
if (response.data !== void 0 && response.data !== "") {
|
|
2329
|
+
outputResponse(response, format);
|
|
2330
|
+
} else {
|
|
2331
|
+
printSuccess("Snapshot cloned.");
|
|
2332
|
+
}
|
|
2333
|
+
})
|
|
2334
|
+
);
|
|
2335
|
+
addExamples(clone, [
|
|
2336
|
+
{
|
|
2337
|
+
description: "Clone a snapshot",
|
|
2338
|
+
command: "geonic snapshots clone <snapshot-id>"
|
|
2339
|
+
}
|
|
2340
|
+
]);
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
// src/commands/admin/tenants.ts
|
|
2344
|
+
function registerTenantsCommand(parent) {
|
|
2345
|
+
const tenants = parent.command("tenants").description("Manage tenants");
|
|
2346
|
+
const list = tenants.command("list").description("List all tenants").action(
|
|
2347
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2348
|
+
const client = createClient(cmd);
|
|
2349
|
+
const format = getFormat(cmd);
|
|
2350
|
+
const response = await client.rawRequest("GET", "/admin/tenants");
|
|
2351
|
+
outputResponse(response, format);
|
|
2352
|
+
})
|
|
2353
|
+
);
|
|
2354
|
+
addExamples(list, [
|
|
2355
|
+
{
|
|
2356
|
+
description: "List all tenants",
|
|
2357
|
+
command: "geonic admin tenants list"
|
|
2358
|
+
}
|
|
2359
|
+
]);
|
|
2360
|
+
const get = tenants.command("get <id>").description("Get a tenant by ID").action(
|
|
2361
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2362
|
+
const client = createClient(cmd);
|
|
2363
|
+
const format = getFormat(cmd);
|
|
2364
|
+
const response = await client.rawRequest(
|
|
2365
|
+
"GET",
|
|
2366
|
+
`/admin/tenants/${encodeURIComponent(String(id))}`
|
|
2367
|
+
);
|
|
2368
|
+
outputResponse(response, format);
|
|
2369
|
+
})
|
|
2370
|
+
);
|
|
2371
|
+
addExamples(get, [
|
|
2372
|
+
{
|
|
2373
|
+
description: "Get a tenant by ID",
|
|
2374
|
+
command: "geonic admin tenants get <tenant-id>"
|
|
2375
|
+
}
|
|
2376
|
+
]);
|
|
2377
|
+
const create = tenants.command("create [json]").description("Create a new tenant").action(
|
|
2378
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2379
|
+
const body = await parseJsonInput(json);
|
|
2380
|
+
const client = createClient(cmd);
|
|
2381
|
+
const format = getFormat(cmd);
|
|
2382
|
+
const response = await client.rawRequest("POST", "/admin/tenants", {
|
|
2383
|
+
body
|
|
2384
|
+
});
|
|
2385
|
+
outputResponse(response, format);
|
|
2386
|
+
printSuccess("Tenant created.");
|
|
2387
|
+
})
|
|
2388
|
+
);
|
|
2389
|
+
addExamples(create, [
|
|
2390
|
+
{
|
|
2391
|
+
description: "Create a tenant from a JSON file",
|
|
2392
|
+
command: "geonic admin tenants create @tenant.json"
|
|
2393
|
+
}
|
|
2394
|
+
]);
|
|
2395
|
+
const update = tenants.command("update <id> [json]").description("Update a tenant").action(
|
|
2396
|
+
withErrorHandler(
|
|
2397
|
+
async (id, json, _opts, cmd) => {
|
|
2398
|
+
const body = await parseJsonInput(json);
|
|
2399
|
+
const client = createClient(cmd);
|
|
2400
|
+
const format = getFormat(cmd);
|
|
2401
|
+
const response = await client.rawRequest(
|
|
2402
|
+
"PATCH",
|
|
2403
|
+
`/admin/tenants/${encodeURIComponent(String(id))}`,
|
|
2404
|
+
{ body }
|
|
2405
|
+
);
|
|
2406
|
+
outputResponse(response, format);
|
|
2407
|
+
printSuccess("Tenant updated.");
|
|
2408
|
+
}
|
|
2409
|
+
)
|
|
2410
|
+
);
|
|
2411
|
+
addExamples(update, [
|
|
2412
|
+
{
|
|
2413
|
+
description: "Update a tenant from a JSON file",
|
|
2414
|
+
command: "geonic admin tenants update <tenant-id> @tenant.json"
|
|
2415
|
+
}
|
|
2416
|
+
]);
|
|
2417
|
+
const del = tenants.command("delete <id>").description("Delete a tenant").action(
|
|
2418
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2419
|
+
const client = createClient(cmd);
|
|
2420
|
+
await client.rawRequest(
|
|
2421
|
+
"DELETE",
|
|
2422
|
+
`/admin/tenants/${encodeURIComponent(String(id))}`
|
|
2423
|
+
);
|
|
2424
|
+
printSuccess("Tenant deleted.");
|
|
2425
|
+
})
|
|
2426
|
+
);
|
|
2427
|
+
addExamples(del, [
|
|
2428
|
+
{
|
|
2429
|
+
description: "Delete a tenant",
|
|
2430
|
+
command: "geonic admin tenants delete <tenant-id>"
|
|
2431
|
+
}
|
|
2432
|
+
]);
|
|
2433
|
+
const activate = tenants.command("activate <id>").description("Activate a tenant").action(
|
|
2434
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2435
|
+
const client = createClient(cmd);
|
|
2436
|
+
await client.rawRequest(
|
|
2437
|
+
"POST",
|
|
2438
|
+
`/admin/tenants/${encodeURIComponent(String(id))}/activate`
|
|
2439
|
+
);
|
|
2440
|
+
printSuccess("Tenant activated.");
|
|
2441
|
+
})
|
|
2442
|
+
);
|
|
2443
|
+
addExamples(activate, [
|
|
2444
|
+
{
|
|
2445
|
+
description: "Activate a tenant",
|
|
2446
|
+
command: "geonic admin tenants activate <tenant-id>"
|
|
2447
|
+
}
|
|
2448
|
+
]);
|
|
2449
|
+
const deactivate = tenants.command("deactivate <id>").description("Deactivate a tenant").action(
|
|
2450
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2451
|
+
const client = createClient(cmd);
|
|
2452
|
+
await client.rawRequest(
|
|
2453
|
+
"POST",
|
|
2454
|
+
`/admin/tenants/${encodeURIComponent(String(id))}/deactivate`
|
|
2455
|
+
);
|
|
2456
|
+
printSuccess("Tenant deactivated.");
|
|
2457
|
+
})
|
|
2458
|
+
);
|
|
2459
|
+
addExamples(deactivate, [
|
|
2460
|
+
{
|
|
2461
|
+
description: "Deactivate a tenant",
|
|
2462
|
+
command: "geonic admin tenants deactivate <tenant-id>"
|
|
2463
|
+
}
|
|
2464
|
+
]);
|
|
2465
|
+
}
|
|
2466
|
+
|
|
2467
|
+
// src/commands/admin/users.ts
|
|
2468
|
+
function registerUsersCommand(parent) {
|
|
2469
|
+
const users = parent.command("users").description("Manage users");
|
|
2470
|
+
const list = users.command("list").description("List all users").action(
|
|
2471
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2472
|
+
const client = createClient(cmd);
|
|
2473
|
+
const format = getFormat(cmd);
|
|
2474
|
+
const response = await client.rawRequest("GET", "/admin/users");
|
|
2475
|
+
outputResponse(response, format);
|
|
2476
|
+
})
|
|
2477
|
+
);
|
|
2478
|
+
addExamples(list, [
|
|
2479
|
+
{
|
|
2480
|
+
description: "List all users",
|
|
2481
|
+
command: "geonic admin users list"
|
|
2482
|
+
}
|
|
2483
|
+
]);
|
|
2484
|
+
const get = users.command("get <id>").description("Get a user by ID").action(
|
|
2485
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2486
|
+
const client = createClient(cmd);
|
|
2487
|
+
const format = getFormat(cmd);
|
|
2488
|
+
const response = await client.rawRequest(
|
|
2489
|
+
"GET",
|
|
2490
|
+
`/admin/users/${encodeURIComponent(String(id))}`
|
|
2491
|
+
);
|
|
2492
|
+
outputResponse(response, format);
|
|
2493
|
+
})
|
|
2494
|
+
);
|
|
2495
|
+
addExamples(get, [
|
|
2496
|
+
{
|
|
2497
|
+
description: "Get a user by ID",
|
|
2498
|
+
command: "geonic admin users get <user-id>"
|
|
2499
|
+
}
|
|
2500
|
+
]);
|
|
2501
|
+
const create = users.command("create [json]").description("Create a new user").action(
|
|
2502
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2503
|
+
const body = await parseJsonInput(json);
|
|
2504
|
+
const client = createClient(cmd);
|
|
2505
|
+
const format = getFormat(cmd);
|
|
2506
|
+
const response = await client.rawRequest("POST", "/admin/users", {
|
|
2507
|
+
body
|
|
2508
|
+
});
|
|
2509
|
+
outputResponse(response, format);
|
|
2510
|
+
printSuccess("User created.");
|
|
2511
|
+
})
|
|
2512
|
+
);
|
|
2513
|
+
addExamples(create, [
|
|
2514
|
+
{
|
|
2515
|
+
description: "Create a user from a JSON file",
|
|
2516
|
+
command: "geonic admin users create @user.json"
|
|
2517
|
+
}
|
|
2518
|
+
]);
|
|
2519
|
+
const update = users.command("update <id> [json]").description("Update a user").action(
|
|
2520
|
+
withErrorHandler(
|
|
2521
|
+
async (id, json, _opts, cmd) => {
|
|
2522
|
+
const body = await parseJsonInput(json);
|
|
2523
|
+
const client = createClient(cmd);
|
|
2524
|
+
const format = getFormat(cmd);
|
|
2525
|
+
const response = await client.rawRequest(
|
|
2526
|
+
"PATCH",
|
|
2527
|
+
`/admin/users/${encodeURIComponent(String(id))}`,
|
|
2528
|
+
{ body }
|
|
2529
|
+
);
|
|
2530
|
+
outputResponse(response, format);
|
|
2531
|
+
printSuccess("User updated.");
|
|
2532
|
+
}
|
|
2533
|
+
)
|
|
2534
|
+
);
|
|
2535
|
+
addExamples(update, [
|
|
2536
|
+
{
|
|
2537
|
+
description: "Update a user from a JSON file",
|
|
2538
|
+
command: "geonic admin users update <user-id> @user.json"
|
|
2539
|
+
}
|
|
2540
|
+
]);
|
|
2541
|
+
const del = users.command("delete <id>").description("Delete a user").action(
|
|
2542
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2543
|
+
const client = createClient(cmd);
|
|
2544
|
+
await client.rawRequest(
|
|
2545
|
+
"DELETE",
|
|
2546
|
+
`/admin/users/${encodeURIComponent(String(id))}`
|
|
2547
|
+
);
|
|
2548
|
+
printSuccess("User deleted.");
|
|
2549
|
+
})
|
|
2550
|
+
);
|
|
2551
|
+
addExamples(del, [
|
|
2552
|
+
{
|
|
2553
|
+
description: "Delete a user",
|
|
2554
|
+
command: "geonic admin users delete <user-id>"
|
|
2555
|
+
}
|
|
2556
|
+
]);
|
|
2557
|
+
const activate = users.command("activate <id>").description("Activate a user").action(
|
|
2558
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2559
|
+
const client = createClient(cmd);
|
|
2560
|
+
await client.rawRequest(
|
|
2561
|
+
"POST",
|
|
2562
|
+
`/admin/users/${encodeURIComponent(String(id))}/activate`
|
|
2563
|
+
);
|
|
2564
|
+
printSuccess("User activated.");
|
|
2565
|
+
})
|
|
2566
|
+
);
|
|
2567
|
+
addExamples(activate, [
|
|
2568
|
+
{
|
|
2569
|
+
description: "Activate a user",
|
|
2570
|
+
command: "geonic admin users activate <user-id>"
|
|
2571
|
+
}
|
|
2572
|
+
]);
|
|
2573
|
+
const deactivate = users.command("deactivate <id>").description("Deactivate a user").action(
|
|
2574
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2575
|
+
const client = createClient(cmd);
|
|
2576
|
+
await client.rawRequest(
|
|
2577
|
+
"POST",
|
|
2578
|
+
`/admin/users/${encodeURIComponent(String(id))}/deactivate`
|
|
2579
|
+
);
|
|
2580
|
+
printSuccess("User deactivated.");
|
|
2581
|
+
})
|
|
2582
|
+
);
|
|
2583
|
+
addExamples(deactivate, [
|
|
2584
|
+
{
|
|
2585
|
+
description: "Deactivate a user",
|
|
2586
|
+
command: "geonic admin users deactivate <user-id>"
|
|
2587
|
+
}
|
|
2588
|
+
]);
|
|
2589
|
+
const unlock = users.command("unlock <id>").description("Unlock a user").action(
|
|
2590
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2591
|
+
const client = createClient(cmd);
|
|
2592
|
+
await client.rawRequest(
|
|
2593
|
+
"POST",
|
|
2594
|
+
`/admin/users/${encodeURIComponent(String(id))}/unlock`
|
|
2595
|
+
);
|
|
2596
|
+
printSuccess("User unlocked.");
|
|
2597
|
+
})
|
|
2598
|
+
);
|
|
2599
|
+
addExamples(unlock, [
|
|
2600
|
+
{
|
|
2601
|
+
description: "Unlock a locked user account",
|
|
2602
|
+
command: "geonic admin users unlock <user-id>"
|
|
2603
|
+
}
|
|
2604
|
+
]);
|
|
2605
|
+
}
|
|
2606
|
+
|
|
2607
|
+
// src/commands/admin/policies.ts
|
|
2608
|
+
function registerPoliciesCommand(parent) {
|
|
2609
|
+
const policies = parent.command("policies").description("Manage policies");
|
|
2610
|
+
const list = policies.command("list").description("List all policies").action(
|
|
2611
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2612
|
+
const client = createClient(cmd);
|
|
2613
|
+
const format = getFormat(cmd);
|
|
2614
|
+
const response = await client.rawRequest("GET", "/admin/policies");
|
|
2615
|
+
outputResponse(response, format);
|
|
2616
|
+
})
|
|
2617
|
+
);
|
|
2618
|
+
addExamples(list, [
|
|
2619
|
+
{
|
|
2620
|
+
description: "List all policies",
|
|
2621
|
+
command: "geonic admin policies list"
|
|
2622
|
+
}
|
|
2623
|
+
]);
|
|
2624
|
+
const get = policies.command("get <id>").description("Get a policy by ID").action(
|
|
2625
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2626
|
+
const client = createClient(cmd);
|
|
2627
|
+
const format = getFormat(cmd);
|
|
2628
|
+
const response = await client.rawRequest(
|
|
2629
|
+
"GET",
|
|
2630
|
+
`/admin/policies/${encodeURIComponent(String(id))}`
|
|
2631
|
+
);
|
|
2632
|
+
outputResponse(response, format);
|
|
2633
|
+
})
|
|
2634
|
+
);
|
|
2635
|
+
addExamples(get, [
|
|
2636
|
+
{
|
|
2637
|
+
description: "Get a policy by ID",
|
|
2638
|
+
command: "geonic admin policies get <policy-id>"
|
|
2639
|
+
}
|
|
2640
|
+
]);
|
|
2641
|
+
const create = policies.command("create [json]").description("Create a new policy").action(
|
|
2642
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2643
|
+
const body = await parseJsonInput(json);
|
|
2644
|
+
const client = createClient(cmd);
|
|
2645
|
+
const format = getFormat(cmd);
|
|
2646
|
+
const response = await client.rawRequest("POST", "/admin/policies", {
|
|
2647
|
+
body
|
|
2648
|
+
});
|
|
2649
|
+
outputResponse(response, format);
|
|
2650
|
+
printSuccess("Policy created.");
|
|
2651
|
+
})
|
|
2652
|
+
);
|
|
2653
|
+
addExamples(create, [
|
|
2654
|
+
{
|
|
2655
|
+
description: "Create a policy from a JSON file",
|
|
2656
|
+
command: "geonic admin policies create @policy.json"
|
|
2657
|
+
}
|
|
2658
|
+
]);
|
|
2659
|
+
const update = policies.command("update <id> [json]").description("Update a policy").action(
|
|
2660
|
+
withErrorHandler(
|
|
2661
|
+
async (id, json, _opts, cmd) => {
|
|
2662
|
+
const body = await parseJsonInput(json);
|
|
2663
|
+
const client = createClient(cmd);
|
|
2664
|
+
const format = getFormat(cmd);
|
|
2665
|
+
const response = await client.rawRequest(
|
|
2666
|
+
"PATCH",
|
|
2667
|
+
`/admin/policies/${encodeURIComponent(String(id))}`,
|
|
2668
|
+
{ body }
|
|
2669
|
+
);
|
|
2670
|
+
outputResponse(response, format);
|
|
2671
|
+
printSuccess("Policy updated.");
|
|
2672
|
+
}
|
|
2673
|
+
)
|
|
2674
|
+
);
|
|
2675
|
+
addExamples(update, [
|
|
2676
|
+
{
|
|
2677
|
+
description: "Update a policy from a JSON file",
|
|
2678
|
+
command: "geonic admin policies update <policy-id> @policy.json"
|
|
2679
|
+
}
|
|
2680
|
+
]);
|
|
2681
|
+
const del = policies.command("delete <id>").description("Delete a policy").action(
|
|
2682
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2683
|
+
const client = createClient(cmd);
|
|
2684
|
+
await client.rawRequest(
|
|
2685
|
+
"DELETE",
|
|
2686
|
+
`/admin/policies/${encodeURIComponent(String(id))}`
|
|
2687
|
+
);
|
|
2688
|
+
printSuccess("Policy deleted.");
|
|
2689
|
+
})
|
|
2690
|
+
);
|
|
2691
|
+
addExamples(del, [
|
|
2692
|
+
{
|
|
2693
|
+
description: "Delete a policy",
|
|
2694
|
+
command: "geonic admin policies delete <policy-id>"
|
|
2695
|
+
}
|
|
2696
|
+
]);
|
|
2697
|
+
const activate = policies.command("activate <id>").description("Activate a policy").action(
|
|
2698
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2699
|
+
const client = createClient(cmd);
|
|
2700
|
+
await client.rawRequest(
|
|
2701
|
+
"POST",
|
|
2702
|
+
`/admin/policies/${encodeURIComponent(String(id))}/activate`
|
|
2703
|
+
);
|
|
2704
|
+
printSuccess("Policy activated.");
|
|
2705
|
+
})
|
|
2706
|
+
);
|
|
2707
|
+
addExamples(activate, [
|
|
2708
|
+
{
|
|
2709
|
+
description: "Activate a policy",
|
|
2710
|
+
command: "geonic admin policies activate <policy-id>"
|
|
2711
|
+
}
|
|
2712
|
+
]);
|
|
2713
|
+
const deactivate = policies.command("deactivate <id>").description("Deactivate a policy").action(
|
|
2714
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2715
|
+
const client = createClient(cmd);
|
|
2716
|
+
await client.rawRequest(
|
|
2717
|
+
"POST",
|
|
2718
|
+
`/admin/policies/${encodeURIComponent(String(id))}/deactivate`
|
|
2719
|
+
);
|
|
2720
|
+
printSuccess("Policy deactivated.");
|
|
2721
|
+
})
|
|
2722
|
+
);
|
|
2723
|
+
addExamples(deactivate, [
|
|
2724
|
+
{
|
|
2725
|
+
description: "Deactivate a policy",
|
|
2726
|
+
command: "geonic admin policies deactivate <policy-id>"
|
|
2727
|
+
}
|
|
2728
|
+
]);
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
// src/commands/admin/oauth-clients.ts
|
|
2732
|
+
function registerOAuthClientsCommand(parent) {
|
|
2733
|
+
const oauthClients = parent.command("oauth-clients").description("Manage OAuth clients");
|
|
2734
|
+
const list = oauthClients.command("list").description("List all OAuth clients").action(
|
|
2735
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2736
|
+
const client = createClient(cmd);
|
|
2737
|
+
const format = getFormat(cmd);
|
|
2738
|
+
const response = await client.rawRequest("GET", "/admin/oauth-clients");
|
|
2739
|
+
outputResponse(response, format);
|
|
2740
|
+
})
|
|
2741
|
+
);
|
|
2742
|
+
addExamples(list, [
|
|
2743
|
+
{
|
|
2744
|
+
description: "List all OAuth clients",
|
|
2745
|
+
command: "geonic admin oauth-clients list"
|
|
2746
|
+
}
|
|
2747
|
+
]);
|
|
2748
|
+
const get = oauthClients.command("get <id>").description("Get an OAuth client by ID").action(
|
|
2749
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2750
|
+
const client = createClient(cmd);
|
|
2751
|
+
const format = getFormat(cmd);
|
|
2752
|
+
const response = await client.rawRequest(
|
|
2753
|
+
"GET",
|
|
2754
|
+
`/admin/oauth-clients/${encodeURIComponent(String(id))}`
|
|
2755
|
+
);
|
|
2756
|
+
outputResponse(response, format);
|
|
2757
|
+
})
|
|
2758
|
+
);
|
|
2759
|
+
addExamples(get, [
|
|
2760
|
+
{
|
|
2761
|
+
description: "Get an OAuth client by ID",
|
|
2762
|
+
command: "geonic admin oauth-clients get <client-id>"
|
|
2763
|
+
}
|
|
2764
|
+
]);
|
|
2765
|
+
const create = oauthClients.command("create [json]").description("Create a new OAuth client").action(
|
|
2766
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2767
|
+
const body = await parseJsonInput(json);
|
|
2768
|
+
const client = createClient(cmd);
|
|
2769
|
+
const format = getFormat(cmd);
|
|
2770
|
+
const response = await client.rawRequest("POST", "/admin/oauth-clients", {
|
|
2771
|
+
body
|
|
2772
|
+
});
|
|
2773
|
+
outputResponse(response, format);
|
|
2774
|
+
printSuccess("OAuth client created.");
|
|
2775
|
+
})
|
|
2776
|
+
);
|
|
2777
|
+
addExamples(create, [
|
|
2778
|
+
{
|
|
2779
|
+
description: "Create an OAuth client from a JSON file",
|
|
2780
|
+
command: "geonic admin oauth-clients create @client.json"
|
|
2781
|
+
}
|
|
2782
|
+
]);
|
|
2783
|
+
const update = oauthClients.command("update <id> [json]").description("Update an OAuth client").action(
|
|
2784
|
+
withErrorHandler(
|
|
2785
|
+
async (id, json, _opts, cmd) => {
|
|
2786
|
+
const body = await parseJsonInput(json);
|
|
2787
|
+
const client = createClient(cmd);
|
|
2788
|
+
const format = getFormat(cmd);
|
|
2789
|
+
const response = await client.rawRequest(
|
|
2790
|
+
"PATCH",
|
|
2791
|
+
`/admin/oauth-clients/${encodeURIComponent(String(id))}`,
|
|
2792
|
+
{ body }
|
|
2793
|
+
);
|
|
2794
|
+
outputResponse(response, format);
|
|
2795
|
+
printSuccess("OAuth client updated.");
|
|
2796
|
+
}
|
|
2797
|
+
)
|
|
2798
|
+
);
|
|
2799
|
+
addExamples(update, [
|
|
2800
|
+
{
|
|
2801
|
+
description: "Update an OAuth client from a JSON file",
|
|
2802
|
+
command: "geonic admin oauth-clients update <client-id> @client.json"
|
|
2803
|
+
}
|
|
2804
|
+
]);
|
|
2805
|
+
const del = oauthClients.command("delete <id>").description("Delete an OAuth client").action(
|
|
2806
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2807
|
+
const client = createClient(cmd);
|
|
2808
|
+
await client.rawRequest(
|
|
2809
|
+
"DELETE",
|
|
2810
|
+
`/admin/oauth-clients/${encodeURIComponent(String(id))}`
|
|
2811
|
+
);
|
|
2812
|
+
printSuccess("OAuth client deleted.");
|
|
2813
|
+
})
|
|
2814
|
+
);
|
|
2815
|
+
addExamples(del, [
|
|
2816
|
+
{
|
|
2817
|
+
description: "Delete an OAuth client",
|
|
2818
|
+
command: "geonic admin oauth-clients delete <client-id>"
|
|
2819
|
+
}
|
|
2820
|
+
]);
|
|
2821
|
+
}
|
|
2822
|
+
function registerCaddeCommand(parent) {
|
|
2823
|
+
const cadde = parent.command("cadde").description("Manage CADDE configuration");
|
|
2824
|
+
const caddeGet = cadde.command("get").description("Get CADDE configuration").action(
|
|
2825
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2826
|
+
const client = createClient(cmd);
|
|
2827
|
+
const format = getFormat(cmd);
|
|
2828
|
+
const response = await client.rawRequest("GET", "/admin/cadde");
|
|
2829
|
+
outputResponse(response, format);
|
|
2830
|
+
})
|
|
2831
|
+
);
|
|
2832
|
+
addExamples(caddeGet, [
|
|
2833
|
+
{
|
|
2834
|
+
description: "Get CADDE configuration",
|
|
2835
|
+
command: "geonic admin cadde get"
|
|
2836
|
+
}
|
|
2837
|
+
]);
|
|
2838
|
+
const caddeSet = cadde.command("set [json]").description("Set CADDE configuration").action(
|
|
2839
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2840
|
+
const body = await parseJsonInput(json);
|
|
2841
|
+
const client = createClient(cmd);
|
|
2842
|
+
const format = getFormat(cmd);
|
|
2843
|
+
const response = await client.rawRequest("PUT", "/admin/cadde", {
|
|
2844
|
+
body
|
|
2845
|
+
});
|
|
2846
|
+
outputResponse(response, format);
|
|
2847
|
+
printSuccess("CADDE configuration set.");
|
|
2848
|
+
})
|
|
2849
|
+
);
|
|
2850
|
+
addExamples(caddeSet, [
|
|
2851
|
+
{
|
|
2852
|
+
description: "Set CADDE configuration from a JSON file",
|
|
2853
|
+
command: "geonic admin cadde set @cadde-config.json"
|
|
2854
|
+
}
|
|
2855
|
+
]);
|
|
2856
|
+
const caddeDelete = cadde.command("delete").description("Delete CADDE configuration").action(
|
|
2857
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2858
|
+
const client = createClient(cmd);
|
|
2859
|
+
await client.rawRequest("DELETE", "/admin/cadde");
|
|
2860
|
+
printSuccess("CADDE configuration deleted.");
|
|
2861
|
+
})
|
|
2862
|
+
);
|
|
2863
|
+
addExamples(caddeDelete, [
|
|
2864
|
+
{
|
|
2865
|
+
description: "Delete CADDE configuration",
|
|
2866
|
+
command: "geonic admin cadde delete"
|
|
2867
|
+
}
|
|
2868
|
+
]);
|
|
2869
|
+
}
|
|
2870
|
+
|
|
2871
|
+
// src/commands/admin/index.ts
|
|
2872
|
+
function registerAdminCommand(program2) {
|
|
2873
|
+
const admin = program2.command("admin").description("Manage admin resources");
|
|
2874
|
+
registerTenantsCommand(admin);
|
|
2875
|
+
registerUsersCommand(admin);
|
|
2876
|
+
registerPoliciesCommand(admin);
|
|
2877
|
+
registerOAuthClientsCommand(admin);
|
|
2878
|
+
registerCaddeCommand(admin);
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
// src/commands/rules.ts
|
|
2882
|
+
function registerRulesCommand(program2) {
|
|
2883
|
+
const rules = program2.command("rules").description("Manage rule engine");
|
|
2884
|
+
const list = rules.command("list").description("List all rules").action(
|
|
2885
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
2886
|
+
const client = createClient(cmd);
|
|
2887
|
+
const format = getFormat(cmd);
|
|
2888
|
+
const response = await client.rawRequest("GET", "/rules");
|
|
2889
|
+
outputResponse(response, format);
|
|
2890
|
+
})
|
|
2891
|
+
);
|
|
2892
|
+
addExamples(list, [
|
|
2893
|
+
{
|
|
2894
|
+
description: "List all rules",
|
|
2895
|
+
command: "geonic rules list"
|
|
2896
|
+
}
|
|
2897
|
+
]);
|
|
2898
|
+
const get = rules.command("get <id>").description("Get a rule by ID").action(
|
|
2899
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2900
|
+
const client = createClient(cmd);
|
|
2901
|
+
const format = getFormat(cmd);
|
|
2902
|
+
const response = await client.rawRequest(
|
|
2903
|
+
"GET",
|
|
2904
|
+
`/rules/${encodeURIComponent(String(id))}`
|
|
2905
|
+
);
|
|
2906
|
+
outputResponse(response, format);
|
|
2907
|
+
})
|
|
2908
|
+
);
|
|
2909
|
+
addExamples(get, [
|
|
2910
|
+
{
|
|
2911
|
+
description: "Get a specific rule",
|
|
2912
|
+
command: "geonic rules get <rule-id>"
|
|
2913
|
+
}
|
|
2914
|
+
]);
|
|
2915
|
+
const create = rules.command("create [json]").description("Create a new rule").action(
|
|
2916
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
2917
|
+
const body = await parseJsonInput(json);
|
|
2918
|
+
const client = createClient(cmd);
|
|
2919
|
+
const format = getFormat(cmd);
|
|
2920
|
+
const response = await client.rawRequest("POST", "/rules", { body });
|
|
2921
|
+
outputResponse(response, format);
|
|
2922
|
+
printSuccess("Rule created.");
|
|
2923
|
+
})
|
|
2924
|
+
);
|
|
2925
|
+
addExamples(create, [
|
|
2926
|
+
{
|
|
2927
|
+
description: "Create a rule from a file",
|
|
2928
|
+
command: "geonic rules create @rule.json"
|
|
2929
|
+
}
|
|
2930
|
+
]);
|
|
2931
|
+
const update = rules.command("update <id> [json]").description("Update a rule").action(
|
|
2932
|
+
withErrorHandler(
|
|
2933
|
+
async (id, json, _opts, cmd) => {
|
|
2934
|
+
const body = await parseJsonInput(json);
|
|
2935
|
+
const client = createClient(cmd);
|
|
2936
|
+
const format = getFormat(cmd);
|
|
2937
|
+
const response = await client.rawRequest(
|
|
2938
|
+
"PATCH",
|
|
2939
|
+
`/rules/${encodeURIComponent(String(id))}`,
|
|
2940
|
+
{ body }
|
|
2941
|
+
);
|
|
2942
|
+
outputResponse(response, format);
|
|
2943
|
+
printSuccess("Rule updated.");
|
|
2944
|
+
}
|
|
2945
|
+
)
|
|
2946
|
+
);
|
|
2947
|
+
addExamples(update, [
|
|
2948
|
+
{
|
|
2949
|
+
description: "Update a rule from a file",
|
|
2950
|
+
command: "geonic rules update <rule-id> @rule.json"
|
|
2951
|
+
}
|
|
2952
|
+
]);
|
|
2953
|
+
const del = rules.command("delete <id>").description("Delete a rule").action(
|
|
2954
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2955
|
+
const client = createClient(cmd);
|
|
2956
|
+
await client.rawRequest(
|
|
2957
|
+
"DELETE",
|
|
2958
|
+
`/rules/${encodeURIComponent(String(id))}`
|
|
2959
|
+
);
|
|
2960
|
+
printSuccess("Rule deleted.");
|
|
2961
|
+
})
|
|
2962
|
+
);
|
|
2963
|
+
addExamples(del, [
|
|
2964
|
+
{
|
|
2965
|
+
description: "Delete a rule",
|
|
2966
|
+
command: "geonic rules delete <rule-id>"
|
|
2967
|
+
}
|
|
2968
|
+
]);
|
|
2969
|
+
const activate = rules.command("activate <id>").description("Activate a rule").action(
|
|
2970
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2971
|
+
const client = createClient(cmd);
|
|
2972
|
+
await client.rawRequest(
|
|
2973
|
+
"POST",
|
|
2974
|
+
`/rules/${encodeURIComponent(String(id))}/activate`
|
|
2975
|
+
);
|
|
2976
|
+
printSuccess("Rule activated.");
|
|
2977
|
+
})
|
|
2978
|
+
);
|
|
2979
|
+
addExamples(activate, [
|
|
2980
|
+
{
|
|
2981
|
+
description: "Activate a rule",
|
|
2982
|
+
command: "geonic rules activate <rule-id>"
|
|
2983
|
+
}
|
|
2984
|
+
]);
|
|
2985
|
+
const deactivate = rules.command("deactivate <id>").description("Deactivate a rule").action(
|
|
2986
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
2987
|
+
const client = createClient(cmd);
|
|
2988
|
+
await client.rawRequest(
|
|
2989
|
+
"POST",
|
|
2990
|
+
`/rules/${encodeURIComponent(String(id))}/deactivate`
|
|
2991
|
+
);
|
|
2992
|
+
printSuccess("Rule deactivated.");
|
|
2993
|
+
})
|
|
2994
|
+
);
|
|
2995
|
+
addExamples(deactivate, [
|
|
2996
|
+
{
|
|
2997
|
+
description: "Deactivate a rule",
|
|
2998
|
+
command: "geonic rules deactivate <rule-id>"
|
|
2999
|
+
}
|
|
3000
|
+
]);
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
// src/commands/models.ts
|
|
3004
|
+
function registerModelsCommand(program2) {
|
|
3005
|
+
const models = program2.command("custom-data-models").alias("models").description("Manage custom data models");
|
|
3006
|
+
const list = models.command("list").description("List all models").action(
|
|
3007
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
3008
|
+
const client = createClient(cmd);
|
|
3009
|
+
const format = getFormat(cmd);
|
|
3010
|
+
const response = await client.rawRequest("GET", "/custom-data-models");
|
|
3011
|
+
outputResponse(response, format);
|
|
3012
|
+
})
|
|
3013
|
+
);
|
|
3014
|
+
addExamples(list, [
|
|
3015
|
+
{
|
|
3016
|
+
description: "List all models",
|
|
3017
|
+
command: "geonic models list"
|
|
3018
|
+
}
|
|
3019
|
+
]);
|
|
3020
|
+
const get = models.command("get <id>").description("Get a model by ID").action(
|
|
3021
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
3022
|
+
const client = createClient(cmd);
|
|
3023
|
+
const format = getFormat(cmd);
|
|
3024
|
+
const response = await client.rawRequest(
|
|
3025
|
+
"GET",
|
|
3026
|
+
`/custom-data-models/${encodeURIComponent(String(id))}`
|
|
3027
|
+
);
|
|
3028
|
+
outputResponse(response, format);
|
|
3029
|
+
})
|
|
3030
|
+
);
|
|
3031
|
+
addExamples(get, [
|
|
3032
|
+
{
|
|
3033
|
+
description: "Get a specific model",
|
|
3034
|
+
command: "geonic models get <model-id>"
|
|
3035
|
+
}
|
|
3036
|
+
]);
|
|
3037
|
+
const create = models.command("create [json]").description("Create a new model").action(
|
|
3038
|
+
withErrorHandler(async (json, _opts, cmd) => {
|
|
3039
|
+
const body = await parseJsonInput(json);
|
|
3040
|
+
const client = createClient(cmd);
|
|
3041
|
+
const format = getFormat(cmd);
|
|
3042
|
+
const response = await client.rawRequest("POST", "/custom-data-models", { body });
|
|
3043
|
+
outputResponse(response, format);
|
|
3044
|
+
printSuccess("Model created.");
|
|
3045
|
+
})
|
|
3046
|
+
);
|
|
3047
|
+
addExamples(create, [
|
|
3048
|
+
{
|
|
3049
|
+
description: "Create a model from a file",
|
|
3050
|
+
command: "geonic models create @model.json"
|
|
3051
|
+
}
|
|
3052
|
+
]);
|
|
3053
|
+
const update = models.command("update <id> [json]").description("Update a model").action(
|
|
3054
|
+
withErrorHandler(
|
|
3055
|
+
async (id, json, _opts, cmd) => {
|
|
3056
|
+
const body = await parseJsonInput(json);
|
|
3057
|
+
const client = createClient(cmd);
|
|
3058
|
+
const format = getFormat(cmd);
|
|
3059
|
+
const response = await client.rawRequest(
|
|
3060
|
+
"PATCH",
|
|
3061
|
+
`/custom-data-models/${encodeURIComponent(String(id))}`,
|
|
3062
|
+
{ body }
|
|
3063
|
+
);
|
|
3064
|
+
outputResponse(response, format);
|
|
3065
|
+
printSuccess("Model updated.");
|
|
3066
|
+
}
|
|
3067
|
+
)
|
|
3068
|
+
);
|
|
3069
|
+
addExamples(update, [
|
|
3070
|
+
{
|
|
3071
|
+
description: "Update a model from a file",
|
|
3072
|
+
command: "geonic models update <model-id> @model.json"
|
|
3073
|
+
}
|
|
3074
|
+
]);
|
|
3075
|
+
const del = models.command("delete <id>").description("Delete a model").action(
|
|
3076
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
3077
|
+
const client = createClient(cmd);
|
|
3078
|
+
await client.rawRequest(
|
|
3079
|
+
"DELETE",
|
|
3080
|
+
`/custom-data-models/${encodeURIComponent(String(id))}`
|
|
3081
|
+
);
|
|
3082
|
+
printSuccess("Model deleted.");
|
|
3083
|
+
})
|
|
3084
|
+
);
|
|
3085
|
+
addExamples(del, [
|
|
3086
|
+
{
|
|
3087
|
+
description: "Delete a model",
|
|
3088
|
+
command: "geonic models delete <model-id>"
|
|
3089
|
+
}
|
|
3090
|
+
]);
|
|
3091
|
+
}
|
|
3092
|
+
|
|
3093
|
+
// src/commands/catalog.ts
|
|
3094
|
+
function registerCatalogCommand(program2) {
|
|
3095
|
+
const catalog = program2.command("catalog").description("Browse DCAT-AP catalog");
|
|
3096
|
+
const get = catalog.command("get").description("Get the catalog").action(
|
|
3097
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
3098
|
+
const client = createClient(cmd);
|
|
3099
|
+
const format = getFormat(cmd);
|
|
3100
|
+
const response = await client.rawRequest("GET", "/catalog");
|
|
3101
|
+
outputResponse(response, format);
|
|
3102
|
+
})
|
|
3103
|
+
);
|
|
3104
|
+
addExamples(get, [
|
|
3105
|
+
{
|
|
3106
|
+
description: "Get the DCAT-AP catalog",
|
|
3107
|
+
command: "geonic catalog get"
|
|
3108
|
+
}
|
|
3109
|
+
]);
|
|
3110
|
+
const datasets = catalog.command("datasets").description("Manage catalog datasets");
|
|
3111
|
+
const datasetsList = datasets.command("list").description("List all datasets").action(
|
|
3112
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
3113
|
+
const client = createClient(cmd);
|
|
3114
|
+
const format = getFormat(cmd);
|
|
3115
|
+
const response = await client.rawRequest("GET", "/catalog/datasets");
|
|
3116
|
+
outputResponse(response, format);
|
|
3117
|
+
})
|
|
3118
|
+
);
|
|
3119
|
+
addExamples(datasetsList, [
|
|
3120
|
+
{
|
|
3121
|
+
description: "List all catalog datasets",
|
|
3122
|
+
command: "geonic catalog datasets list"
|
|
3123
|
+
}
|
|
3124
|
+
]);
|
|
3125
|
+
const datasetsGet = datasets.command("get <id>").description("Get a dataset by ID").action(
|
|
3126
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
3127
|
+
const client = createClient(cmd);
|
|
3128
|
+
const format = getFormat(cmd);
|
|
3129
|
+
const response = await client.rawRequest(
|
|
3130
|
+
"GET",
|
|
3131
|
+
`/catalog/datasets/${encodeURIComponent(String(id))}`
|
|
3132
|
+
);
|
|
3133
|
+
outputResponse(response, format);
|
|
3134
|
+
})
|
|
3135
|
+
);
|
|
3136
|
+
addExamples(datasetsGet, [
|
|
3137
|
+
{
|
|
3138
|
+
description: "Get a specific dataset",
|
|
3139
|
+
command: "geonic catalog datasets get <dataset-id>"
|
|
3140
|
+
}
|
|
3141
|
+
]);
|
|
3142
|
+
const datasetsSample = datasets.command("sample <id>").description("Get sample data for a dataset").action(
|
|
3143
|
+
withErrorHandler(async (id, _opts, cmd) => {
|
|
3144
|
+
const client = createClient(cmd);
|
|
3145
|
+
const format = getFormat(cmd);
|
|
3146
|
+
const response = await client.rawRequest(
|
|
3147
|
+
"GET",
|
|
3148
|
+
`/catalog/datasets/${encodeURIComponent(String(id))}/sample`
|
|
3149
|
+
);
|
|
3150
|
+
outputResponse(response, format);
|
|
3151
|
+
})
|
|
3152
|
+
);
|
|
3153
|
+
addExamples(datasetsSample, [
|
|
3154
|
+
{
|
|
3155
|
+
description: "Get sample data for a dataset",
|
|
3156
|
+
command: "geonic catalog datasets sample <dataset-id>"
|
|
3157
|
+
}
|
|
3158
|
+
]);
|
|
3159
|
+
}
|
|
3160
|
+
|
|
3161
|
+
// src/commands/health.ts
|
|
3162
|
+
import { createRequire } from "module";
|
|
3163
|
+
function registerHealthCommand(program2) {
|
|
3164
|
+
const health = program2.command("health").description("Check the health status of the server").action(
|
|
3165
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
3166
|
+
const client = createClient(cmd);
|
|
3167
|
+
const format = getFormat(cmd);
|
|
3168
|
+
const response = await client.rawRequest("GET", "/health");
|
|
3169
|
+
outputResponse(response, format);
|
|
3170
|
+
})
|
|
3171
|
+
);
|
|
3172
|
+
addExamples(health, [
|
|
3173
|
+
{
|
|
3174
|
+
description: "Check server health",
|
|
3175
|
+
command: "geonic health"
|
|
3176
|
+
}
|
|
3177
|
+
]);
|
|
3178
|
+
}
|
|
3179
|
+
function registerVersionCommand(program2) {
|
|
3180
|
+
const version = program2.command("version").description("Display CLI and server version information").action(
|
|
3181
|
+
withErrorHandler(async (_opts, cmd) => {
|
|
3182
|
+
const require2 = createRequire(import.meta.url);
|
|
3183
|
+
const pkg = require2("../package.json");
|
|
3184
|
+
const cliVersion = pkg.version;
|
|
3185
|
+
printInfo(`CLI version: ${cliVersion}`);
|
|
3186
|
+
const client = createClient(cmd);
|
|
3187
|
+
const format = getFormat(cmd);
|
|
3188
|
+
const response = await client.rawRequest("GET", "/version");
|
|
3189
|
+
printInfo("Server version:");
|
|
3190
|
+
outputResponse(response, format);
|
|
3191
|
+
})
|
|
3192
|
+
);
|
|
3193
|
+
addExamples(version, [
|
|
3194
|
+
{
|
|
3195
|
+
description: "Show CLI and server version",
|
|
3196
|
+
command: "geonic version"
|
|
3197
|
+
}
|
|
3198
|
+
]);
|
|
3199
|
+
}
|
|
3200
|
+
|
|
3201
|
+
// src/commands/cli.ts
|
|
3202
|
+
import { createRequire as createRequire2 } from "module";
|
|
3203
|
+
function findOption(cmd, program2, flag) {
|
|
3204
|
+
return cmd.options.find((o) => o.long === flag || o.short === flag) || program2.options.find((o) => o.long === flag || o.short === flag);
|
|
3205
|
+
}
|
|
3206
|
+
function optionTakesValue(cmd, program2, flag) {
|
|
3207
|
+
const opt = findOption(cmd, program2, flag);
|
|
3208
|
+
if (!opt) return false;
|
|
3209
|
+
return !!(opt.required || opt.optional);
|
|
3210
|
+
}
|
|
3211
|
+
function findSubcommand(cmd, name) {
|
|
3212
|
+
return cmd.commands.find(
|
|
3213
|
+
(c) => c.name() === name || c.aliases().includes(name)
|
|
3214
|
+
);
|
|
3215
|
+
}
|
|
3216
|
+
function generateCompletions(program2, line, point) {
|
|
3217
|
+
const truncated = line.slice(0, point);
|
|
3218
|
+
const tokens = truncated.trim().split(/\s+/).filter(Boolean);
|
|
3219
|
+
const startingNewWord = /\s$/.test(truncated);
|
|
3220
|
+
const partial = !startingNewWord && tokens.length > 1 ? tokens[tokens.length - 1] : "";
|
|
3221
|
+
const walkTokens = startingNewWord ? tokens.slice(1) : tokens.slice(1, -1);
|
|
3222
|
+
let currentCmd = program2;
|
|
3223
|
+
let i = 0;
|
|
3224
|
+
while (i < walkTokens.length) {
|
|
3225
|
+
const token = walkTokens[i];
|
|
3226
|
+
if (token.startsWith("-")) {
|
|
3227
|
+
if (optionTakesValue(currentCmd, program2, token)) {
|
|
3228
|
+
i += 2;
|
|
3229
|
+
} else {
|
|
3230
|
+
i += 1;
|
|
3231
|
+
}
|
|
3232
|
+
continue;
|
|
3233
|
+
}
|
|
3234
|
+
const sub = findSubcommand(currentCmd, token);
|
|
3235
|
+
if (sub) {
|
|
3236
|
+
currentCmd = sub;
|
|
3237
|
+
}
|
|
3238
|
+
i++;
|
|
3239
|
+
}
|
|
3240
|
+
if (currentCmd.name() === "help" && currentCmd.parent === program2) {
|
|
3241
|
+
const helpIdx = walkTokens.findIndex((t) => t === "help");
|
|
3242
|
+
const rawArgs = helpIdx >= 0 ? walkTokens.slice(helpIdx + 1) : [];
|
|
3243
|
+
const helpArgs = [];
|
|
3244
|
+
for (let j = 0; j < rawArgs.length; j++) {
|
|
3245
|
+
const t = rawArgs[j];
|
|
3246
|
+
if (t.startsWith("-")) {
|
|
3247
|
+
if (optionTakesValue(currentCmd, program2, t)) {
|
|
3248
|
+
j++;
|
|
3249
|
+
}
|
|
3250
|
+
continue;
|
|
3251
|
+
}
|
|
3252
|
+
helpArgs.push(t);
|
|
3253
|
+
}
|
|
3254
|
+
let target = program2;
|
|
3255
|
+
for (const arg of helpArgs) {
|
|
3256
|
+
const sub = findSubcommand(target, arg);
|
|
3257
|
+
if (sub) target = sub;
|
|
3258
|
+
else break;
|
|
3259
|
+
}
|
|
3260
|
+
const subs = target.commands.filter((c) => !c._hidden).map((c) => c.name());
|
|
3261
|
+
return subs.filter((s) => s.startsWith(partial));
|
|
3262
|
+
}
|
|
3263
|
+
const predecessor = startingNewWord ? tokens[tokens.length - 1] : tokens.length >= 2 ? tokens[tokens.length - 2] : void 0;
|
|
3264
|
+
if (predecessor && predecessor.startsWith("-") && optionTakesValue(currentCmd, program2, predecessor)) {
|
|
3265
|
+
return getOptionValueCompletions(predecessor, partial);
|
|
3266
|
+
}
|
|
3267
|
+
if (partial.startsWith("-")) {
|
|
3268
|
+
return getOptionCompletions(currentCmd, program2, partial);
|
|
3269
|
+
}
|
|
3270
|
+
const subcommands = currentCmd.commands.filter((c) => !c._hidden).map((c) => c.name());
|
|
3271
|
+
if (subcommands.length > 0) {
|
|
3272
|
+
return subcommands.filter((s) => s.startsWith(partial));
|
|
3273
|
+
}
|
|
3274
|
+
for (const arg of currentCmd.registeredArguments) {
|
|
3275
|
+
if (arg.description && arg.description.includes("@file")) {
|
|
3276
|
+
return ["<file>"];
|
|
3277
|
+
}
|
|
3278
|
+
}
|
|
3279
|
+
return [];
|
|
3280
|
+
}
|
|
3281
|
+
function getOptionCompletions(cmd, program2, partial) {
|
|
3282
|
+
const options = /* @__PURE__ */ new Set();
|
|
3283
|
+
for (const opt of cmd.options) {
|
|
3284
|
+
if (opt.hidden) continue;
|
|
3285
|
+
if (opt.long) options.add(opt.long);
|
|
3286
|
+
}
|
|
3287
|
+
for (const opt of program2.options) {
|
|
3288
|
+
if (opt.hidden) continue;
|
|
3289
|
+
if (opt.long) options.add(opt.long);
|
|
3290
|
+
}
|
|
3291
|
+
return [...options].filter((o) => o.startsWith(partial));
|
|
3292
|
+
}
|
|
3293
|
+
function getOptionValueCompletions(optionFlag, partial) {
|
|
3294
|
+
if (optionFlag === "--format" || optionFlag === "-f") {
|
|
3295
|
+
return ["json", "table", "geojson"].filter(
|
|
3296
|
+
(v) => v.startsWith(partial)
|
|
3297
|
+
);
|
|
3298
|
+
}
|
|
3299
|
+
return [];
|
|
3300
|
+
}
|
|
3301
|
+
var ZSH_SCRIPT = `_geonic_completions() {
|
|
3302
|
+
local completions
|
|
3303
|
+
completions=(\${(f)"$(geonic cli completions --line="$BUFFER" --point="$CURSOR" 2>/dev/null)"})
|
|
3304
|
+
if [[ "\${completions[1]}" == "<file>" ]]; then
|
|
3305
|
+
_files
|
|
3306
|
+
return
|
|
3307
|
+
fi
|
|
3308
|
+
compadd -a completions
|
|
3309
|
+
}
|
|
3310
|
+
compdef _geonic_completions geonic`;
|
|
3311
|
+
var BASH_SCRIPT = `_geonic_completions() {
|
|
3312
|
+
local cur="\${COMP_WORDS[COMP_CWORD]}"
|
|
3313
|
+
local completions
|
|
3314
|
+
completions=$(geonic cli completions --line="\${COMP_LINE}" --point="\${COMP_POINT}" 2>/dev/null)
|
|
3315
|
+
if [[ "$completions" == "<file>" ]]; then
|
|
3316
|
+
COMPREPLY=()
|
|
3317
|
+
return
|
|
3318
|
+
fi
|
|
3319
|
+
COMPREPLY=($(compgen -W "$completions" -- "$cur"))
|
|
3320
|
+
}
|
|
3321
|
+
complete -o default -F _geonic_completions geonic`;
|
|
3322
|
+
function registerCliCommand(program2) {
|
|
3323
|
+
const cli = program2.command("cli").summary("Manage CLI internals").description("Manage CLI internals such as shell completions.");
|
|
3324
|
+
const completions = cli.command("completions").summary("Generate shell completions").description("Generate shell completions for geonic CLI.").option("--line <line>", "Current command line content").option("--point <point>", "Cursor position in the command line").action((opts) => {
|
|
3325
|
+
if (opts.line !== void 0 && opts.point !== void 0) {
|
|
3326
|
+
const point = parseInt(opts.point, 10);
|
|
3327
|
+
const results = generateCompletions(program2, opts.line, point);
|
|
3328
|
+
for (const r of results) {
|
|
3329
|
+
console.log(r);
|
|
3330
|
+
}
|
|
3331
|
+
}
|
|
3332
|
+
});
|
|
3333
|
+
const bash = completions.command("bash").description("Output bash completion script").action(() => {
|
|
3334
|
+
console.log(BASH_SCRIPT);
|
|
3335
|
+
});
|
|
3336
|
+
addExamples(bash, [
|
|
3337
|
+
{
|
|
3338
|
+
description: "Print the bash completion script",
|
|
3339
|
+
command: "geonic cli completions bash"
|
|
3340
|
+
},
|
|
3341
|
+
{
|
|
3342
|
+
description: "Enable in current shell session",
|
|
3343
|
+
command: 'eval "$(geonic cli completions bash)"'
|
|
3344
|
+
},
|
|
3345
|
+
{
|
|
3346
|
+
description: "Persist in ~/.bashrc",
|
|
3347
|
+
command: `echo 'eval "$(geonic cli completions bash)"' >> ~/.bashrc`
|
|
3348
|
+
}
|
|
3349
|
+
]);
|
|
3350
|
+
const zsh = completions.command("zsh").description("Output zsh completion script").action(() => {
|
|
3351
|
+
console.log(ZSH_SCRIPT);
|
|
3352
|
+
});
|
|
3353
|
+
addExamples(zsh, [
|
|
3354
|
+
{
|
|
3355
|
+
description: "Print the zsh completion script",
|
|
3356
|
+
command: "geonic cli completions zsh"
|
|
3357
|
+
},
|
|
3358
|
+
{
|
|
3359
|
+
description: "Enable in current shell session",
|
|
3360
|
+
command: 'eval "$(geonic cli completions zsh)"'
|
|
3361
|
+
},
|
|
3362
|
+
{
|
|
3363
|
+
description: "Persist in ~/.zshrc",
|
|
3364
|
+
command: `echo 'eval "$(geonic cli completions zsh)"' >> ~/.zshrc`
|
|
3365
|
+
}
|
|
3366
|
+
]);
|
|
3367
|
+
const version = cli.command("version").description("Display the CLI version").action(() => {
|
|
3368
|
+
const require2 = createRequire2(import.meta.url);
|
|
3369
|
+
const pkg = require2("../package.json");
|
|
3370
|
+
console.log(pkg.version);
|
|
3371
|
+
});
|
|
3372
|
+
addExamples(version, [
|
|
3373
|
+
{
|
|
3374
|
+
description: "Show CLI version",
|
|
3375
|
+
command: "geonic cli version"
|
|
3376
|
+
}
|
|
3377
|
+
]);
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
// src/cli.ts
|
|
3381
|
+
function createProgram() {
|
|
3382
|
+
const require2 = createRequire3(import.meta.url);
|
|
3383
|
+
const pkg = require2("../package.json");
|
|
3384
|
+
const program2 = new Command2();
|
|
3385
|
+
program2.name("geonic").description(pkg.description).option("-u, --url <url>", "Base URL of the GeonicDB server").option("-s, --service <name>", "NGSILD-Tenant header").option("--token <token>", "Authentication token").option("-p, --profile <name>", "Use a named profile").option("--api-key <key>", "API key for authentication").option("-f, --format <fmt>", "Output format: json, table, geojson").option("--no-color", "Disable color output").option("-v, --verbose", "Verbose output");
|
|
3386
|
+
registerHelpCommand(program2);
|
|
3387
|
+
registerConfigCommand(program2);
|
|
3388
|
+
registerAuthCommands(program2);
|
|
3389
|
+
registerProfileCommands(program2);
|
|
3390
|
+
registerEntitiesCommand(program2);
|
|
3391
|
+
registerBatchCommand(program2);
|
|
3392
|
+
registerSubscriptionsCommand(program2);
|
|
3393
|
+
registerRegistrationsCommand(program2);
|
|
3394
|
+
registerTypesCommand(program2);
|
|
3395
|
+
registerTemporalCommand(program2);
|
|
3396
|
+
registerSnapshotsCommand(program2);
|
|
3397
|
+
registerAdminCommand(program2);
|
|
3398
|
+
registerRulesCommand(program2);
|
|
3399
|
+
registerModelsCommand(program2);
|
|
3400
|
+
registerCatalogCommand(program2);
|
|
3401
|
+
registerHealthCommand(program2);
|
|
3402
|
+
registerVersionCommand(program2);
|
|
3403
|
+
registerCliCommand(program2);
|
|
3404
|
+
const hiddenAttrs = new Command2("attrs").description("Manage entity attributes");
|
|
3405
|
+
addAttrsSubcommands(hiddenAttrs);
|
|
3406
|
+
program2.addCommand(hiddenAttrs, { hidden: true });
|
|
3407
|
+
return program2;
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
// src/index.ts
|
|
3411
|
+
var program = createProgram();
|
|
3412
|
+
program.parse();
|
|
3413
|
+
//# sourceMappingURL=index.js.map
|