aranea-sdk-cli 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/auth.d.ts +26 -0
- package/dist/commands/auth.js +427 -0
- package/dist/commands/schema.d.ts +9 -5
- package/dist/commands/schema.js +239 -157
- package/dist/config.d.ts +36 -1
- package/dist/config.js +70 -3
- package/dist/index.js +4 -1
- package/package.json +1 -1
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AraneaSDK CLI - Auth Command
|
|
3
|
+
*
|
|
4
|
+
* Firebase Auth token management for CLI operations
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* aranea-sdk auth login # Interactive login
|
|
8
|
+
* aranea-sdk auth token # Show current token info
|
|
9
|
+
* aranea-sdk auth refresh # Refresh token
|
|
10
|
+
* aranea-sdk auth logout # Clear saved credentials
|
|
11
|
+
*
|
|
12
|
+
* Token Storage:
|
|
13
|
+
* ~/.aranea-sdk/credentials.json
|
|
14
|
+
*
|
|
15
|
+
* Note: ID tokens expire after 1 hour. Use `auth refresh` to get a new token.
|
|
16
|
+
*/
|
|
17
|
+
import { Command } from 'commander';
|
|
18
|
+
/**
|
|
19
|
+
* Get valid token, refreshing if necessary
|
|
20
|
+
* Exported for use by other commands
|
|
21
|
+
*/
|
|
22
|
+
export declare function getValidToken(): Promise<string | null>;
|
|
23
|
+
/**
|
|
24
|
+
* Auth command
|
|
25
|
+
*/
|
|
26
|
+
export declare const authCommand: Command;
|
|
@@ -0,0 +1,427 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* AraneaSDK CLI - Auth Command
|
|
4
|
+
*
|
|
5
|
+
* Firebase Auth token management for CLI operations
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* aranea-sdk auth login # Interactive login
|
|
9
|
+
* aranea-sdk auth token # Show current token info
|
|
10
|
+
* aranea-sdk auth refresh # Refresh token
|
|
11
|
+
* aranea-sdk auth logout # Clear saved credentials
|
|
12
|
+
*
|
|
13
|
+
* Token Storage:
|
|
14
|
+
* ~/.aranea-sdk/credentials.json
|
|
15
|
+
*
|
|
16
|
+
* Note: ID tokens expire after 1 hour. Use `auth refresh` to get a new token.
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
52
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
53
|
+
};
|
|
54
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
55
|
+
exports.authCommand = void 0;
|
|
56
|
+
exports.getValidToken = getValidToken;
|
|
57
|
+
const commander_1 = require("commander");
|
|
58
|
+
const fs = __importStar(require("fs"));
|
|
59
|
+
const path = __importStar(require("path"));
|
|
60
|
+
const os = __importStar(require("os"));
|
|
61
|
+
const readline = __importStar(require("readline"));
|
|
62
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
63
|
+
// Firebase Auth REST API configuration
|
|
64
|
+
const FIREBASE_API_KEY = 'AIzaSyChVMfLPZ9fL_LQBK2TtLbM2iONPhyU1NY'; // mobesorder project
|
|
65
|
+
const AUTH_ENDPOINT = 'https://identitytoolkit.googleapis.com/v1';
|
|
66
|
+
// Credentials storage
|
|
67
|
+
const CONFIG_DIR = path.join(os.homedir(), '.aranea-sdk');
|
|
68
|
+
const CREDENTIALS_FILE = path.join(CONFIG_DIR, 'credentials.json');
|
|
69
|
+
/**
|
|
70
|
+
* Ensure config directory exists
|
|
71
|
+
*/
|
|
72
|
+
function ensureConfigDir() {
|
|
73
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
74
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Load saved credentials
|
|
79
|
+
*/
|
|
80
|
+
function loadCredentials() {
|
|
81
|
+
try {
|
|
82
|
+
if (fs.existsSync(CREDENTIALS_FILE)) {
|
|
83
|
+
const data = fs.readFileSync(CREDENTIALS_FILE, 'utf8');
|
|
84
|
+
return JSON.parse(data);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
// Ignore parse errors
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Save credentials to disk
|
|
94
|
+
*/
|
|
95
|
+
function saveCredentials(credentials) {
|
|
96
|
+
ensureConfigDir();
|
|
97
|
+
fs.writeFileSync(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), {
|
|
98
|
+
mode: 0o600,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Clear saved credentials
|
|
103
|
+
*/
|
|
104
|
+
function clearCredentials() {
|
|
105
|
+
if (fs.existsSync(CREDENTIALS_FILE)) {
|
|
106
|
+
fs.unlinkSync(CREDENTIALS_FILE);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if token is expired or about to expire (within 5 minutes)
|
|
111
|
+
*/
|
|
112
|
+
function isTokenExpired(credentials) {
|
|
113
|
+
const bufferMs = 5 * 60 * 1000; // 5 minutes buffer
|
|
114
|
+
return Date.now() >= credentials.expiresAt - bufferMs;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Format remaining time
|
|
118
|
+
*/
|
|
119
|
+
function formatRemainingTime(expiresAt) {
|
|
120
|
+
const remaining = expiresAt - Date.now();
|
|
121
|
+
if (remaining <= 0) {
|
|
122
|
+
return chalk_1.default.red('Expired');
|
|
123
|
+
}
|
|
124
|
+
const minutes = Math.floor(remaining / 60000);
|
|
125
|
+
const seconds = Math.floor((remaining % 60000) / 1000);
|
|
126
|
+
if (minutes > 30) {
|
|
127
|
+
return chalk_1.default.green(`${minutes}m ${seconds}s`);
|
|
128
|
+
}
|
|
129
|
+
else if (minutes > 5) {
|
|
130
|
+
return chalk_1.default.yellow(`${minutes}m ${seconds}s`);
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
return chalk_1.default.red(`${minutes}m ${seconds}s`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Prompt for password (hidden input)
|
|
138
|
+
*/
|
|
139
|
+
async function promptPassword(prompt) {
|
|
140
|
+
return new Promise((resolve) => {
|
|
141
|
+
const rl = readline.createInterface({
|
|
142
|
+
input: process.stdin,
|
|
143
|
+
output: process.stdout,
|
|
144
|
+
});
|
|
145
|
+
// Hide password input
|
|
146
|
+
const stdin = process.stdin;
|
|
147
|
+
const wasRaw = stdin.isRaw || false;
|
|
148
|
+
process.stdout.write(prompt);
|
|
149
|
+
if (stdin.isTTY) {
|
|
150
|
+
stdin.setRawMode(true);
|
|
151
|
+
}
|
|
152
|
+
let password = '';
|
|
153
|
+
const onData = (char) => {
|
|
154
|
+
const c = char.toString();
|
|
155
|
+
switch (c) {
|
|
156
|
+
case '\n':
|
|
157
|
+
case '\r':
|
|
158
|
+
case '\u0004': // Ctrl+D
|
|
159
|
+
if (stdin.isTTY) {
|
|
160
|
+
stdin.setRawMode(wasRaw);
|
|
161
|
+
}
|
|
162
|
+
stdin.removeListener('data', onData);
|
|
163
|
+
rl.close();
|
|
164
|
+
process.stdout.write('\n');
|
|
165
|
+
resolve(password);
|
|
166
|
+
break;
|
|
167
|
+
case '\u0003': // Ctrl+C
|
|
168
|
+
process.exit();
|
|
169
|
+
break;
|
|
170
|
+
case '\u007F': // Backspace
|
|
171
|
+
if (password.length > 0) {
|
|
172
|
+
password = password.slice(0, -1);
|
|
173
|
+
process.stdout.write('\b \b');
|
|
174
|
+
}
|
|
175
|
+
break;
|
|
176
|
+
default:
|
|
177
|
+
password += c;
|
|
178
|
+
process.stdout.write('*');
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
stdin.on('data', onData);
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Prompt for email
|
|
187
|
+
*/
|
|
188
|
+
async function promptEmail() {
|
|
189
|
+
return new Promise((resolve) => {
|
|
190
|
+
const rl = readline.createInterface({
|
|
191
|
+
input: process.stdin,
|
|
192
|
+
output: process.stdout,
|
|
193
|
+
});
|
|
194
|
+
rl.question('Email: ', (answer) => {
|
|
195
|
+
rl.close();
|
|
196
|
+
resolve(answer.trim());
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Login with email and password
|
|
202
|
+
*/
|
|
203
|
+
async function loginWithEmailPassword(email, password) {
|
|
204
|
+
const response = await fetch(`${AUTH_ENDPOINT}/accounts:signInWithPassword?key=${FIREBASE_API_KEY}`, {
|
|
205
|
+
method: 'POST',
|
|
206
|
+
headers: { 'Content-Type': 'application/json' },
|
|
207
|
+
body: JSON.stringify({
|
|
208
|
+
email,
|
|
209
|
+
password,
|
|
210
|
+
returnSecureToken: true,
|
|
211
|
+
}),
|
|
212
|
+
});
|
|
213
|
+
if (!response.ok) {
|
|
214
|
+
const errorBody = await response.json();
|
|
215
|
+
const errorMessage = errorBody.error?.message || 'Login failed';
|
|
216
|
+
throw new Error(translateFirebaseError(errorMessage));
|
|
217
|
+
}
|
|
218
|
+
const data = (await response.json());
|
|
219
|
+
const expiresIn = parseInt(data.expiresIn, 10) * 1000; // Convert to ms
|
|
220
|
+
return {
|
|
221
|
+
idToken: data.idToken,
|
|
222
|
+
refreshToken: data.refreshToken,
|
|
223
|
+
email: data.email,
|
|
224
|
+
expiresAt: Date.now() + expiresIn,
|
|
225
|
+
localId: data.localId,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Refresh ID token
|
|
230
|
+
*/
|
|
231
|
+
async function refreshIdToken(refreshToken) {
|
|
232
|
+
const response = await fetch(`https://securetoken.googleapis.com/v1/token?key=${FIREBASE_API_KEY}`, {
|
|
233
|
+
method: 'POST',
|
|
234
|
+
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
235
|
+
body: `grant_type=refresh_token&refresh_token=${encodeURIComponent(refreshToken)}`,
|
|
236
|
+
});
|
|
237
|
+
if (!response.ok) {
|
|
238
|
+
const errorBody = await response.json();
|
|
239
|
+
throw new Error(errorBody.error?.message || 'Token refresh failed');
|
|
240
|
+
}
|
|
241
|
+
const data = (await response.json());
|
|
242
|
+
const expiresIn = parseInt(data.expires_in, 10) * 1000;
|
|
243
|
+
// Load existing credentials to preserve email
|
|
244
|
+
const existing = loadCredentials();
|
|
245
|
+
return {
|
|
246
|
+
idToken: data.id_token,
|
|
247
|
+
refreshToken: data.refresh_token,
|
|
248
|
+
email: existing?.email || '',
|
|
249
|
+
expiresAt: Date.now() + expiresIn,
|
|
250
|
+
localId: data.user_id,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Translate Firebase error messages to user-friendly Japanese
|
|
255
|
+
*/
|
|
256
|
+
function translateFirebaseError(errorCode) {
|
|
257
|
+
const translations = {
|
|
258
|
+
EMAIL_NOT_FOUND: 'メールアドレスが見つかりません',
|
|
259
|
+
INVALID_PASSWORD: 'パスワードが正しくありません',
|
|
260
|
+
INVALID_EMAIL: 'メールアドレスの形式が正しくありません',
|
|
261
|
+
USER_DISABLED: 'このアカウントは無効化されています',
|
|
262
|
+
TOO_MANY_ATTEMPTS_TRY_LATER: 'ログイン試行回数が多すぎます。しばらく待ってから再試行してください',
|
|
263
|
+
INVALID_LOGIN_CREDENTIALS: 'メールアドレスまたはパスワードが正しくありません',
|
|
264
|
+
TOKEN_EXPIRED: 'トークンの有効期限が切れています。再度ログインしてください',
|
|
265
|
+
INVALID_REFRESH_TOKEN: 'リフレッシュトークンが無効です。再度ログインしてください',
|
|
266
|
+
};
|
|
267
|
+
return translations[errorCode] || errorCode;
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get valid token, refreshing if necessary
|
|
271
|
+
* Exported for use by other commands
|
|
272
|
+
*/
|
|
273
|
+
async function getValidToken() {
|
|
274
|
+
const credentials = loadCredentials();
|
|
275
|
+
if (!credentials) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
if (isTokenExpired(credentials)) {
|
|
279
|
+
try {
|
|
280
|
+
const newCredentials = await refreshIdToken(credentials.refreshToken);
|
|
281
|
+
saveCredentials(newCredentials);
|
|
282
|
+
return newCredentials.idToken;
|
|
283
|
+
}
|
|
284
|
+
catch {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
return credentials.idToken;
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Auth command
|
|
292
|
+
*/
|
|
293
|
+
exports.authCommand = new commander_1.Command('auth')
|
|
294
|
+
.description('Firebase Auth token management')
|
|
295
|
+
.addHelpText('after', `
|
|
296
|
+
Token Lifecycle:
|
|
297
|
+
- ID tokens expire after 1 hour
|
|
298
|
+
- Use 'auth refresh' to get a new token
|
|
299
|
+
- Tokens are auto-saved to ~/.aranea-sdk/credentials.json
|
|
300
|
+
|
|
301
|
+
Examples:
|
|
302
|
+
aranea-sdk auth login # Interactive login
|
|
303
|
+
aranea-sdk auth token # Show current token info
|
|
304
|
+
aranea-sdk auth refresh # Refresh expired token
|
|
305
|
+
aranea-sdk auth logout # Clear saved credentials
|
|
306
|
+
`);
|
|
307
|
+
// auth login
|
|
308
|
+
exports.authCommand
|
|
309
|
+
.command('login')
|
|
310
|
+
.description('Login with email and password')
|
|
311
|
+
.option('-e, --email <email>', 'Email address')
|
|
312
|
+
.action(async (options) => {
|
|
313
|
+
console.log(chalk_1.default.cyan('\n=== AraneaSDK CLI Login ===\n'));
|
|
314
|
+
try {
|
|
315
|
+
// Get email
|
|
316
|
+
const email = options.email || (await promptEmail());
|
|
317
|
+
if (!email) {
|
|
318
|
+
console.error(chalk_1.default.red('Email is required'));
|
|
319
|
+
process.exit(1);
|
|
320
|
+
}
|
|
321
|
+
// Get password
|
|
322
|
+
const password = await promptPassword('Password: ');
|
|
323
|
+
if (!password) {
|
|
324
|
+
console.error(chalk_1.default.red('Password is required'));
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
console.log('\nLogging in...');
|
|
328
|
+
const credentials = await loginWithEmailPassword(email, password);
|
|
329
|
+
saveCredentials(credentials);
|
|
330
|
+
console.log(chalk_1.default.green('\n✓ Login successful!'));
|
|
331
|
+
console.log(` Email: ${credentials.email}`);
|
|
332
|
+
console.log(` User ID: ${credentials.localId}`);
|
|
333
|
+
console.log(` Token expires: ${formatRemainingTime(credentials.expiresAt)}`);
|
|
334
|
+
console.log(`\nCredentials saved to: ${CREDENTIALS_FILE}`);
|
|
335
|
+
console.log(chalk_1.default.yellow('\nNote: ID tokens expire after 1 hour. Use "aranea-sdk auth refresh" to renew.'));
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
console.error(chalk_1.default.red(`\n✖ Login failed: ${error.message}`));
|
|
339
|
+
process.exit(1);
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
// auth token
|
|
343
|
+
exports.authCommand
|
|
344
|
+
.command('token')
|
|
345
|
+
.description('Show current token information')
|
|
346
|
+
.option('--raw', 'Output raw token value only')
|
|
347
|
+
.action(async (options) => {
|
|
348
|
+
const credentials = loadCredentials();
|
|
349
|
+
if (!credentials) {
|
|
350
|
+
if (options.raw) {
|
|
351
|
+
process.exit(1);
|
|
352
|
+
}
|
|
353
|
+
console.log(chalk_1.default.yellow('\nNo saved credentials found.'));
|
|
354
|
+
console.log('Run "aranea-sdk auth login" to authenticate.');
|
|
355
|
+
process.exit(1);
|
|
356
|
+
}
|
|
357
|
+
if (options.raw) {
|
|
358
|
+
// Output just the token for scripting
|
|
359
|
+
if (isTokenExpired(credentials)) {
|
|
360
|
+
try {
|
|
361
|
+
const newCredentials = await refreshIdToken(credentials.refreshToken);
|
|
362
|
+
saveCredentials(newCredentials);
|
|
363
|
+
console.log(newCredentials.idToken);
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
process.exit(1);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
console.log(credentials.idToken);
|
|
371
|
+
}
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
console.log(chalk_1.default.cyan('\n=== AraneaSDK CLI Token Info ===\n'));
|
|
375
|
+
console.log(` Email: ${credentials.email}`);
|
|
376
|
+
console.log(` User ID: ${credentials.localId}`);
|
|
377
|
+
console.log(` Expires in: ${formatRemainingTime(credentials.expiresAt)}`);
|
|
378
|
+
console.log(` Expires at: ${new Date(credentials.expiresAt).toLocaleString('ja-JP')}`);
|
|
379
|
+
if (isTokenExpired(credentials)) {
|
|
380
|
+
console.log(chalk_1.default.red('\n⚠ Token is expired or about to expire.'));
|
|
381
|
+
console.log('Run "aranea-sdk auth refresh" to get a new token.');
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
console.log(chalk_1.default.green('\n✓ Token is valid'));
|
|
385
|
+
}
|
|
386
|
+
console.log(`\nToken (first 50 chars): ${credentials.idToken.substring(0, 50)}...`);
|
|
387
|
+
});
|
|
388
|
+
// auth refresh
|
|
389
|
+
exports.authCommand
|
|
390
|
+
.command('refresh')
|
|
391
|
+
.description('Refresh the ID token')
|
|
392
|
+
.action(async () => {
|
|
393
|
+
const credentials = loadCredentials();
|
|
394
|
+
if (!credentials) {
|
|
395
|
+
console.log(chalk_1.default.yellow('\nNo saved credentials found.'));
|
|
396
|
+
console.log('Run "aranea-sdk auth login" to authenticate.');
|
|
397
|
+
process.exit(1);
|
|
398
|
+
}
|
|
399
|
+
console.log('Refreshing token...');
|
|
400
|
+
try {
|
|
401
|
+
const newCredentials = await refreshIdToken(credentials.refreshToken);
|
|
402
|
+
saveCredentials(newCredentials);
|
|
403
|
+
console.log(chalk_1.default.green('\n✓ Token refreshed successfully!'));
|
|
404
|
+
console.log(` Email: ${newCredentials.email}`);
|
|
405
|
+
console.log(` Expires in: ${formatRemainingTime(newCredentials.expiresAt)}`);
|
|
406
|
+
console.log(` Expires at: ${new Date(newCredentials.expiresAt).toLocaleString('ja-JP')}`);
|
|
407
|
+
}
|
|
408
|
+
catch (error) {
|
|
409
|
+
console.error(chalk_1.default.red(`\n✖ Token refresh failed: ${error.message}`));
|
|
410
|
+
console.log('You may need to run "aranea-sdk auth login" again.');
|
|
411
|
+
process.exit(1);
|
|
412
|
+
}
|
|
413
|
+
});
|
|
414
|
+
// auth logout
|
|
415
|
+
exports.authCommand
|
|
416
|
+
.command('logout')
|
|
417
|
+
.description('Clear saved credentials')
|
|
418
|
+
.action(() => {
|
|
419
|
+
const credentials = loadCredentials();
|
|
420
|
+
if (!credentials) {
|
|
421
|
+
console.log(chalk_1.default.yellow('\nNo saved credentials found.'));
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
clearCredentials();
|
|
425
|
+
console.log(chalk_1.default.green('\n✓ Credentials cleared.'));
|
|
426
|
+
console.log(` Removed: ${CREDENTIALS_FILE}`);
|
|
427
|
+
});
|
|
@@ -2,13 +2,17 @@
|
|
|
2
2
|
* schema command
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
* aranea-sdk schema
|
|
6
|
-
* aranea-sdk schema
|
|
5
|
+
* aranea-sdk schema list [--endpoint staging|production]
|
|
6
|
+
* aranea-sdk schema get --type "aranea_ar-is04a" [--endpoint staging|production]
|
|
7
7
|
* aranea-sdk schema validate --type "aranea_ar-is04a" --file state.json
|
|
8
8
|
* aranea-sdk schema validate-schema --file schema.json
|
|
9
|
-
* aranea-sdk schema push --file schema.json [--token TOKEN]
|
|
10
|
-
* aranea-sdk schema promote --type "aranea_ar-is04a" [--token TOKEN]
|
|
11
|
-
* aranea-sdk schema info --type "aranea_ar-is04a"
|
|
9
|
+
* aranea-sdk schema push --file schema.json [--token TOKEN] [--endpoint staging|production]
|
|
10
|
+
* aranea-sdk schema promote --type "aranea_ar-is04a" [--token TOKEN] [--endpoint staging|production] [--confirm]
|
|
11
|
+
* aranea-sdk schema info --type "aranea_ar-is04a" [--endpoint staging|production]
|
|
12
|
+
*
|
|
13
|
+
* Environment:
|
|
14
|
+
* - Default: staging (for safety)
|
|
15
|
+
* - Override: ARANEA_ENV=production or --endpoint production
|
|
12
16
|
*/
|
|
13
17
|
import { Command } from 'commander';
|
|
14
18
|
export declare const schemaCommand: Command;
|
package/dist/commands/schema.js
CHANGED
|
@@ -3,13 +3,17 @@
|
|
|
3
3
|
* schema command
|
|
4
4
|
*
|
|
5
5
|
* Usage:
|
|
6
|
-
* aranea-sdk schema
|
|
7
|
-
* aranea-sdk schema
|
|
6
|
+
* aranea-sdk schema list [--endpoint staging|production]
|
|
7
|
+
* aranea-sdk schema get --type "aranea_ar-is04a" [--endpoint staging|production]
|
|
8
8
|
* aranea-sdk schema validate --type "aranea_ar-is04a" --file state.json
|
|
9
9
|
* aranea-sdk schema validate-schema --file schema.json
|
|
10
|
-
* aranea-sdk schema push --file schema.json [--token TOKEN]
|
|
11
|
-
* aranea-sdk schema promote --type "aranea_ar-is04a" [--token TOKEN]
|
|
12
|
-
* aranea-sdk schema info --type "aranea_ar-is04a"
|
|
10
|
+
* aranea-sdk schema push --file schema.json [--token TOKEN] [--endpoint staging|production]
|
|
11
|
+
* aranea-sdk schema promote --type "aranea_ar-is04a" [--token TOKEN] [--endpoint staging|production] [--confirm]
|
|
12
|
+
* aranea-sdk schema info --type "aranea_ar-is04a" [--endpoint staging|production]
|
|
13
|
+
*
|
|
14
|
+
* Environment:
|
|
15
|
+
* - Default: staging (for safety)
|
|
16
|
+
* - Override: ARANEA_ENV=production or --endpoint production
|
|
13
17
|
*/
|
|
14
18
|
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
15
19
|
if (k2 === undefined) k2 = k;
|
|
@@ -55,29 +59,118 @@ const ora_1 = __importDefault(require("ora"));
|
|
|
55
59
|
const axios_1 = __importDefault(require("axios"));
|
|
56
60
|
const fs = __importStar(require("fs"));
|
|
57
61
|
const path = __importStar(require("path"));
|
|
58
|
-
|
|
59
|
-
const
|
|
62
|
+
const readline = __importStar(require("readline"));
|
|
63
|
+
const config_1 = require("../config");
|
|
64
|
+
// Helper: Get API base URL for environment
|
|
65
|
+
function getSchemaApiBase(env) {
|
|
66
|
+
return (0, config_1.getEndpoint)(env).schemaAPI;
|
|
67
|
+
}
|
|
68
|
+
// Helper: Get auth token from options or environment
|
|
69
|
+
function getAuthToken(options) {
|
|
70
|
+
return options.token || process.env.ARANEA_AUTH_TOKEN || process.env.FIREBASE_AUTH_TOKEN || null;
|
|
71
|
+
}
|
|
72
|
+
// Helper: Prompt for confirmation
|
|
73
|
+
async function promptConfirm(message) {
|
|
74
|
+
const rl = readline.createInterface({
|
|
75
|
+
input: process.stdin,
|
|
76
|
+
output: process.stdout,
|
|
77
|
+
});
|
|
78
|
+
return new Promise((resolve) => {
|
|
79
|
+
rl.question(`${message} (y/N): `, (answer) => {
|
|
80
|
+
rl.close();
|
|
81
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
60
85
|
// API Functions
|
|
61
|
-
async function fetchSchemaList() {
|
|
62
|
-
const response = await axios_1.default.get(`${
|
|
86
|
+
async function fetchSchemaList(apiBase) {
|
|
87
|
+
const response = await axios_1.default.get(`${apiBase}?action=list`);
|
|
63
88
|
return response.data;
|
|
64
89
|
}
|
|
65
|
-
async function fetchSchema(type) {
|
|
66
|
-
const response = await axios_1.default.get(`${
|
|
90
|
+
async function fetchSchema(apiBase, type) {
|
|
91
|
+
const response = await axios_1.default.get(`${apiBase}?action=get&type=${encodeURIComponent(type)}`);
|
|
67
92
|
return response.data;
|
|
68
93
|
}
|
|
94
|
+
// Helper: Validate schema file structure (enhanced)
|
|
95
|
+
function validateSchemaStructure(schema) {
|
|
96
|
+
const errors = [];
|
|
97
|
+
const warnings = [];
|
|
98
|
+
// Required top-level fields
|
|
99
|
+
const requiredFields = ['type', 'stateSchema'];
|
|
100
|
+
for (const field of requiredFields) {
|
|
101
|
+
if (!schema[field]) {
|
|
102
|
+
errors.push(`Missing required field: ${field}`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Recommended fields (warn if missing)
|
|
106
|
+
const recommendedFields = ['displayName', 'description', 'productType', 'capabilities'];
|
|
107
|
+
for (const field of recommendedFields) {
|
|
108
|
+
if (!schema[field]) {
|
|
109
|
+
warnings.push(`Recommended field missing: ${field}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Type validation
|
|
113
|
+
if (schema.type && typeof schema.type !== 'string') {
|
|
114
|
+
errors.push('Field "type" must be a string');
|
|
115
|
+
}
|
|
116
|
+
// ProductType validation
|
|
117
|
+
if (schema.productType && !/^\d{3}$/.test(schema.productType)) {
|
|
118
|
+
errors.push('Field "productType" must be a 3-digit string (e.g., "001")');
|
|
119
|
+
}
|
|
120
|
+
// stateSchema validation
|
|
121
|
+
if (schema.stateSchema) {
|
|
122
|
+
if (typeof schema.stateSchema !== 'object') {
|
|
123
|
+
errors.push('Field "stateSchema" must be an object');
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
if (schema.stateSchema.type !== 'object') {
|
|
127
|
+
errors.push('Field "stateSchema.type" must be "object"');
|
|
128
|
+
}
|
|
129
|
+
if (!schema.stateSchema.properties || typeof schema.stateSchema.properties !== 'object') {
|
|
130
|
+
warnings.push('stateSchema.properties is missing or not an object');
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// configSchema validation (optional but if present, must be object)
|
|
135
|
+
if (schema.configSchema && typeof schema.configSchema !== 'object') {
|
|
136
|
+
errors.push('Field "configSchema" must be an object');
|
|
137
|
+
}
|
|
138
|
+
// commandSchema validation (optional but if present, must be object)
|
|
139
|
+
if (schema.commandSchema && typeof schema.commandSchema !== 'object') {
|
|
140
|
+
errors.push('Field "commandSchema" must be an object');
|
|
141
|
+
}
|
|
142
|
+
// capabilities validation (optional but if present, must be array)
|
|
143
|
+
if (schema.capabilities && !Array.isArray(schema.capabilities)) {
|
|
144
|
+
errors.push('Field "capabilities" must be an array');
|
|
145
|
+
}
|
|
146
|
+
// features validation (optional but if present, must be array)
|
|
147
|
+
if (schema.features && !Array.isArray(schema.features)) {
|
|
148
|
+
errors.push('Field "features" must be an array');
|
|
149
|
+
}
|
|
150
|
+
// Warn about client-controlled fields that will be ignored
|
|
151
|
+
if (schema.revisionNumber !== undefined) {
|
|
152
|
+
warnings.push('revisionNumber will be ignored (server-controlled)');
|
|
153
|
+
}
|
|
154
|
+
if (schema.state !== undefined) {
|
|
155
|
+
warnings.push('state will be set to "development" on push (server-controlled)');
|
|
156
|
+
}
|
|
157
|
+
return { valid: errors.length === 0, errors, warnings };
|
|
158
|
+
}
|
|
69
159
|
exports.schemaCommand = new commander_1.Command('schema')
|
|
70
|
-
.description('Type schema retrieval and validation');
|
|
160
|
+
.description('Type schema retrieval and validation (default: staging)');
|
|
71
161
|
// schema list
|
|
72
162
|
exports.schemaCommand
|
|
73
163
|
.command('list')
|
|
74
164
|
.description('List available Type schemas')
|
|
75
|
-
.
|
|
76
|
-
|
|
165
|
+
.option('-e, --endpoint <env>', 'Environment (staging|production)', 'staging')
|
|
166
|
+
.action(async (options) => {
|
|
167
|
+
const env = (0, config_1.resolveEnvironment)(options.endpoint);
|
|
168
|
+
const apiBase = getSchemaApiBase(env);
|
|
169
|
+
const spinner = (0, ora_1.default)(`Fetching schema list from ${env}...`).start();
|
|
77
170
|
try {
|
|
78
|
-
const result = await fetchSchemaList();
|
|
171
|
+
const result = await fetchSchemaList(apiBase);
|
|
79
172
|
spinner.stop();
|
|
80
|
-
console.log(chalk_1.default.bold(
|
|
173
|
+
console.log(chalk_1.default.bold(`\n=== AraneaDevice Type Schema List (${env}) ===\n`));
|
|
81
174
|
if (!result.ok || result.count === 0) {
|
|
82
175
|
console.log(chalk_1.default.yellow('No schemas found.'));
|
|
83
176
|
console.log(chalk_1.default.gray('Run schema registration script to initialize schemas.'));
|
|
@@ -87,13 +180,14 @@ exports.schemaCommand
|
|
|
87
180
|
console.log(chalk_1.default.cyan('Registered Types:'));
|
|
88
181
|
console.log('');
|
|
89
182
|
for (const schema of result.schemas) {
|
|
90
|
-
|
|
91
|
-
console.log(`
|
|
92
|
-
console.log(`
|
|
93
|
-
console.log(`
|
|
183
|
+
const stateIcon = schema.state === 'production' ? chalk_1.default.green('P') : chalk_1.default.yellow('D');
|
|
184
|
+
console.log(` [${stateIcon}] ${chalk_1.default.green(schema.type)}`);
|
|
185
|
+
console.log(` ${schema.displayName} - ${schema.description}`);
|
|
186
|
+
console.log(` ProductType: ${schema.productType}`);
|
|
94
187
|
console.log('');
|
|
95
188
|
}
|
|
96
189
|
console.log(chalk_1.default.gray(`Total: ${result.count} types`));
|
|
190
|
+
console.log(chalk_1.default.gray(`Legend: [${chalk_1.default.green('P')}]=Production [${chalk_1.default.yellow('D')}]=Development`));
|
|
97
191
|
console.log('');
|
|
98
192
|
}
|
|
99
193
|
catch (error) {
|
|
@@ -113,11 +207,14 @@ exports.schemaCommand
|
|
|
113
207
|
.command('get')
|
|
114
208
|
.description('Get detailed schema for a specific Type')
|
|
115
209
|
.requiredOption('-t, --type <type>', 'Type name to retrieve')
|
|
210
|
+
.option('-e, --endpoint <env>', 'Environment (staging|production)', 'staging')
|
|
116
211
|
.option('--json', 'Output as JSON')
|
|
117
212
|
.action(async (options) => {
|
|
118
|
-
const
|
|
213
|
+
const env = (0, config_1.resolveEnvironment)(options.endpoint);
|
|
214
|
+
const apiBase = getSchemaApiBase(env);
|
|
215
|
+
const spinner = (0, ora_1.default)(`Fetching schema for ${options.type} from ${env}...`).start();
|
|
119
216
|
try {
|
|
120
|
-
const result = await fetchSchema(options.type);
|
|
217
|
+
const result = await fetchSchema(apiBase, options.type);
|
|
121
218
|
spinner.stop();
|
|
122
219
|
if (!result.ok) {
|
|
123
220
|
console.error(chalk_1.default.red(`\nSchema "${options.type}" not found`));
|
|
@@ -136,12 +233,19 @@ exports.schemaCommand
|
|
|
136
233
|
console.log(JSON.stringify(schema, null, 2));
|
|
137
234
|
return;
|
|
138
235
|
}
|
|
139
|
-
console.log(chalk_1.default.bold(`\n=== ${schema.type} Schema ===\n`));
|
|
236
|
+
console.log(chalk_1.default.bold(`\n=== ${schema.type} Schema (${env}) ===\n`));
|
|
140
237
|
console.log(`DisplayName: ${chalk_1.default.cyan(schema.displayName)}`);
|
|
141
238
|
console.log(`Description: ${schema.description}`);
|
|
142
239
|
console.log(`ProductType: ${schema.productType}`);
|
|
143
240
|
console.log(`Capabilities: ${schema.capabilities.join(', ')}`);
|
|
144
241
|
console.log(`Version: ${schema.version}`);
|
|
242
|
+
if (schema.state) {
|
|
243
|
+
const stateColor = schema.state === 'production' ? chalk_1.default.green : chalk_1.default.yellow;
|
|
244
|
+
console.log(`State: ${stateColor(schema.state)}`);
|
|
245
|
+
}
|
|
246
|
+
if (schema.revisionNumber) {
|
|
247
|
+
console.log(`Revision: ${schema.revisionNumber}`);
|
|
248
|
+
}
|
|
145
249
|
console.log('');
|
|
146
250
|
// Features
|
|
147
251
|
if (schema.features && schema.features.length > 0) {
|
|
@@ -186,14 +290,6 @@ exports.schemaCommand
|
|
|
186
290
|
if (desc) {
|
|
187
291
|
console.log(` ${desc}`);
|
|
188
292
|
}
|
|
189
|
-
// Show properties if any
|
|
190
|
-
if (def.properties) {
|
|
191
|
-
Object.entries(def.properties).forEach(([propName, propDef]) => {
|
|
192
|
-
const propType = propDef.type || 'any';
|
|
193
|
-
const propDesc = propDef.description || '';
|
|
194
|
-
console.log(` ${chalk_1.default.cyan(propName)}: ${chalk_1.default.yellow(propType)} ${propDesc}`);
|
|
195
|
-
});
|
|
196
|
-
}
|
|
197
293
|
});
|
|
198
294
|
}
|
|
199
295
|
console.log('');
|
|
@@ -201,7 +297,6 @@ exports.schemaCommand
|
|
|
201
297
|
// Sample State Report
|
|
202
298
|
console.log(chalk_1.default.bold('Sample State Report Request:'));
|
|
203
299
|
const sampleState = {};
|
|
204
|
-
// Generate sample values from state schema
|
|
205
300
|
Object.entries(stateProps).forEach(([name, def]) => {
|
|
206
301
|
const type = def.type;
|
|
207
302
|
if (type === 'boolean') {
|
|
@@ -246,20 +341,6 @@ exports.schemaCommand
|
|
|
246
341
|
spinner.fail('Failed to fetch schema');
|
|
247
342
|
if (error.response?.status === 404) {
|
|
248
343
|
console.error(chalk_1.default.red(`\nSchema "${options.type}" not found`));
|
|
249
|
-
// Try to show available types
|
|
250
|
-
try {
|
|
251
|
-
const listResult = await fetchSchemaList();
|
|
252
|
-
if (listResult.ok && listResult.count > 0) {
|
|
253
|
-
console.log('');
|
|
254
|
-
console.log(chalk_1.default.yellow('Available Types:'));
|
|
255
|
-
listResult.schemas.forEach((s) => {
|
|
256
|
-
console.log(` - ${s.type}`);
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
catch {
|
|
261
|
-
// Ignore
|
|
262
|
-
}
|
|
263
344
|
}
|
|
264
345
|
else {
|
|
265
346
|
console.error(chalk_1.default.red(`\nError: ${error.message}`));
|
|
@@ -268,19 +349,20 @@ exports.schemaCommand
|
|
|
268
349
|
process.exit(1);
|
|
269
350
|
}
|
|
270
351
|
});
|
|
271
|
-
// schema validate
|
|
352
|
+
// schema validate (State Report validation)
|
|
272
353
|
exports.schemaCommand
|
|
273
354
|
.command('validate')
|
|
274
355
|
.description('Validate a state report JSON against schema')
|
|
275
356
|
.requiredOption('-t, --type <type>', 'Type name')
|
|
276
357
|
.requiredOption('-f, --file <path>', 'JSON file path')
|
|
358
|
+
.option('-e, --endpoint <env>', 'Environment (staging|production)', 'staging')
|
|
277
359
|
.action(async (options) => {
|
|
360
|
+
const env = (0, config_1.resolveEnvironment)(options.endpoint);
|
|
361
|
+
const apiBase = getSchemaApiBase(env);
|
|
278
362
|
const spinner = (0, ora_1.default)('Validating...').start();
|
|
279
363
|
try {
|
|
280
|
-
const fs = require('fs');
|
|
281
|
-
const path = require('path');
|
|
282
364
|
// Fetch schema from API
|
|
283
|
-
const schemaResult = await fetchSchema(options.type);
|
|
365
|
+
const schemaResult = await fetchSchema(apiBase, options.type);
|
|
284
366
|
if (!schemaResult.ok) {
|
|
285
367
|
spinner.fail(`Schema "${options.type}" not found`);
|
|
286
368
|
process.exit(1);
|
|
@@ -320,7 +402,6 @@ exports.schemaCommand
|
|
|
320
402
|
issues.push(`"${fieldName}": expected number but got ${actualType}`);
|
|
321
403
|
}
|
|
322
404
|
else {
|
|
323
|
-
// Check range
|
|
324
405
|
if (fieldDef.minimum !== undefined && value < fieldDef.minimum) {
|
|
325
406
|
issues.push(`"${fieldName}": value ${value} is below minimum ${fieldDef.minimum}`);
|
|
326
407
|
}
|
|
@@ -387,55 +468,6 @@ exports.schemaCommand
|
|
|
387
468
|
process.exit(1);
|
|
388
469
|
}
|
|
389
470
|
});
|
|
390
|
-
// Helper: Get auth token from options or environment
|
|
391
|
-
function getAuthToken(options) {
|
|
392
|
-
return options.token || process.env.ARANEA_AUTH_TOKEN || process.env.FIREBASE_AUTH_TOKEN || null;
|
|
393
|
-
}
|
|
394
|
-
// Helper: Validate schema file structure
|
|
395
|
-
function validateSchemaStructure(schema) {
|
|
396
|
-
const errors = [];
|
|
397
|
-
// Required top-level fields
|
|
398
|
-
const requiredFields = ['type', 'stateSchema'];
|
|
399
|
-
for (const field of requiredFields) {
|
|
400
|
-
if (!schema[field]) {
|
|
401
|
-
errors.push(`Missing required field: ${field}`);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
// Type validation
|
|
405
|
-
if (schema.type && typeof schema.type !== 'string') {
|
|
406
|
-
errors.push('Field "type" must be a string');
|
|
407
|
-
}
|
|
408
|
-
// ProductType validation
|
|
409
|
-
if (schema.productType && !/^\d{3}$/.test(schema.productType)) {
|
|
410
|
-
errors.push('Field "productType" must be a 3-digit string (e.g., "001")');
|
|
411
|
-
}
|
|
412
|
-
// stateSchema validation
|
|
413
|
-
if (schema.stateSchema) {
|
|
414
|
-
if (typeof schema.stateSchema !== 'object') {
|
|
415
|
-
errors.push('Field "stateSchema" must be an object');
|
|
416
|
-
}
|
|
417
|
-
else if (schema.stateSchema.type !== 'object') {
|
|
418
|
-
errors.push('Field "stateSchema.type" must be "object"');
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
// configSchema validation (optional but if present, must be object)
|
|
422
|
-
if (schema.configSchema && typeof schema.configSchema !== 'object') {
|
|
423
|
-
errors.push('Field "configSchema" must be an object');
|
|
424
|
-
}
|
|
425
|
-
// commandSchema validation (optional but if present, must be object)
|
|
426
|
-
if (schema.commandSchema && typeof schema.commandSchema !== 'object') {
|
|
427
|
-
errors.push('Field "commandSchema" must be an object');
|
|
428
|
-
}
|
|
429
|
-
// capabilities validation (optional but if present, must be array)
|
|
430
|
-
if (schema.capabilities && !Array.isArray(schema.capabilities)) {
|
|
431
|
-
errors.push('Field "capabilities" must be an array');
|
|
432
|
-
}
|
|
433
|
-
// features validation (optional but if present, must be array)
|
|
434
|
-
if (schema.features && !Array.isArray(schema.features)) {
|
|
435
|
-
errors.push('Field "features" must be an array');
|
|
436
|
-
}
|
|
437
|
-
return { valid: errors.length === 0, errors };
|
|
438
|
-
}
|
|
439
471
|
// schema validate-schema - validate a schema definition file
|
|
440
472
|
exports.schemaCommand
|
|
441
473
|
.command('validate-schema')
|
|
@@ -458,14 +490,14 @@ exports.schemaCommand
|
|
|
458
490
|
spinner.fail(`Invalid JSON: ${parseError.message}`);
|
|
459
491
|
process.exit(1);
|
|
460
492
|
}
|
|
461
|
-
const { valid, errors } = validateSchemaStructure(schema);
|
|
493
|
+
const { valid, errors, warnings } = validateSchemaStructure(schema);
|
|
462
494
|
spinner.stop();
|
|
463
495
|
console.log(chalk_1.default.bold('\n=== Schema File Validation ===\n'));
|
|
464
496
|
console.log(`File: ${filePath}`);
|
|
465
497
|
console.log(`Type: ${schema.type || '(not specified)'}`);
|
|
466
498
|
console.log('');
|
|
467
499
|
if (valid) {
|
|
468
|
-
console.log(chalk_1.default.green('
|
|
500
|
+
console.log(chalk_1.default.green('Schema structure is valid'));
|
|
469
501
|
console.log('');
|
|
470
502
|
// Show summary
|
|
471
503
|
console.log(chalk_1.default.bold('Schema Summary:'));
|
|
@@ -478,17 +510,27 @@ exports.schemaCommand
|
|
|
478
510
|
console.log(` Config Fields: ${configFields.length} (${configFields.join(', ') || 'none'})`);
|
|
479
511
|
const cmdFields = Object.keys(schema.commandSchema?.properties || {});
|
|
480
512
|
console.log(` Command Fields: ${cmdFields.length} (${cmdFields.join(', ') || 'none'})`);
|
|
481
|
-
console.log('');
|
|
482
|
-
console.log(chalk_1.default.cyan('Ready to push with: aranea-sdk schema push --file ' + options.file));
|
|
483
513
|
}
|
|
484
514
|
else {
|
|
485
|
-
console.log(chalk_1.default.red('
|
|
515
|
+
console.log(chalk_1.default.red('Schema validation failed'));
|
|
486
516
|
console.log('');
|
|
487
517
|
console.log(chalk_1.default.red('Errors:'));
|
|
488
518
|
errors.forEach((err) => {
|
|
489
519
|
console.log(chalk_1.default.red(` - ${err}`));
|
|
490
520
|
});
|
|
491
521
|
}
|
|
522
|
+
if (warnings.length > 0) {
|
|
523
|
+
console.log('');
|
|
524
|
+
console.log(chalk_1.default.yellow('Warnings:'));
|
|
525
|
+
warnings.forEach((warn) => {
|
|
526
|
+
console.log(chalk_1.default.yellow(` - ${warn}`));
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
console.log('');
|
|
530
|
+
if (valid) {
|
|
531
|
+
console.log(chalk_1.default.cyan('Ready to push with: aranea-sdk schema push --file ' + options.file));
|
|
532
|
+
console.log(chalk_1.default.gray('Note: Use --endpoint production for production environment'));
|
|
533
|
+
}
|
|
492
534
|
console.log('');
|
|
493
535
|
if (!valid) {
|
|
494
536
|
process.exit(1);
|
|
@@ -505,8 +547,13 @@ exports.schemaCommand
|
|
|
505
547
|
.description('Push a schema to Firestore (development state)')
|
|
506
548
|
.requiredOption('-f, --file <path>', 'Schema JSON file path')
|
|
507
549
|
.option('-t, --token <token>', 'Firebase Auth ID token')
|
|
550
|
+
.option('-e, --endpoint <env>', 'Environment (staging|production)', 'staging')
|
|
508
551
|
.option('-d, --dry-run', 'Show what would be pushed without actually pushing')
|
|
509
552
|
.action(async (options) => {
|
|
553
|
+
const env = (0, config_1.resolveEnvironment)(options.endpoint);
|
|
554
|
+
const apiBase = getSchemaApiBase(env);
|
|
555
|
+
// Warn if production
|
|
556
|
+
(0, config_1.warnIfProduction)(env, 'schema push');
|
|
510
557
|
const spinner = (0, ora_1.default)('Preparing schema push...').start();
|
|
511
558
|
try {
|
|
512
559
|
const filePath = path.resolve(options.file);
|
|
@@ -524,13 +571,20 @@ exports.schemaCommand
|
|
|
524
571
|
process.exit(1);
|
|
525
572
|
}
|
|
526
573
|
// Validate schema structure
|
|
527
|
-
const { valid, errors } = validateSchemaStructure(schema);
|
|
574
|
+
const { valid, errors, warnings } = validateSchemaStructure(schema);
|
|
528
575
|
if (!valid) {
|
|
529
576
|
spinner.fail('Schema validation failed');
|
|
530
577
|
console.log('');
|
|
531
578
|
errors.forEach((err) => console.log(chalk_1.default.red(` - ${err}`)));
|
|
532
579
|
process.exit(1);
|
|
533
580
|
}
|
|
581
|
+
// Show warnings
|
|
582
|
+
if (warnings.length > 0 && !options.dryRun) {
|
|
583
|
+
spinner.stop();
|
|
584
|
+
console.log(chalk_1.default.yellow('\nWarnings:'));
|
|
585
|
+
warnings.forEach((warn) => console.log(chalk_1.default.yellow(` - ${warn}`)));
|
|
586
|
+
spinner.start();
|
|
587
|
+
}
|
|
534
588
|
const token = getAuthToken(options);
|
|
535
589
|
if (!token && !options.dryRun) {
|
|
536
590
|
spinner.fail('Authentication required');
|
|
@@ -540,12 +594,12 @@ exports.schemaCommand
|
|
|
540
594
|
console.log(' export ARANEA_AUTH_TOKEN=<TOKEN>');
|
|
541
595
|
console.log(' export FIREBASE_AUTH_TOKEN=<TOKEN>');
|
|
542
596
|
console.log('');
|
|
597
|
+
console.log(chalk_1.default.gray('Token valid for 1 hour. Refresh manually if expired.'));
|
|
543
598
|
console.log(chalk_1.default.gray('Or use --dry-run to see what would be pushed'));
|
|
544
599
|
process.exit(1);
|
|
545
600
|
}
|
|
546
|
-
spinner.text = `Pushing schema: ${schema.type}...`;
|
|
547
|
-
// Prepare the document data
|
|
548
|
-
const now = new Date().toISOString();
|
|
601
|
+
spinner.text = `Pushing schema: ${schema.type} to ${env}...`;
|
|
602
|
+
// Prepare the document data (NO revisionNumber - server will handle it)
|
|
549
603
|
const docData = {
|
|
550
604
|
type: schema.type,
|
|
551
605
|
displayName: schema.displayName || schema.type,
|
|
@@ -559,18 +613,16 @@ exports.schemaCommand
|
|
|
559
613
|
configSchema: schema.configSchema || {},
|
|
560
614
|
commandSchema: schema.commandSchema,
|
|
561
615
|
version: schema.version || 1,
|
|
562
|
-
|
|
563
|
-
revisionNumber: 1, // Will be incremented if already exists
|
|
564
|
-
updatedAt: now,
|
|
565
|
-
updatedBy: 'cli',
|
|
616
|
+
// Note: state and revisionNumber are server-controlled
|
|
566
617
|
};
|
|
567
618
|
if (options.dryRun) {
|
|
568
619
|
spinner.stop();
|
|
569
|
-
console.log(chalk_1.default.bold(
|
|
620
|
+
console.log(chalk_1.default.bold(`\n=== Dry Run: Schema Push (${env}) ===\n`));
|
|
570
621
|
console.log(chalk_1.default.yellow('Would push the following schema:'));
|
|
571
622
|
console.log('');
|
|
572
623
|
console.log(` Target: araneaSDK/typeSettings/schemas/${schema.type}`);
|
|
573
|
-
console.log(`
|
|
624
|
+
console.log(` Environment: ${env}`);
|
|
625
|
+
console.log(` State: ${chalk_1.default.cyan('development')} (server-assigned)`);
|
|
574
626
|
console.log('');
|
|
575
627
|
console.log('Document Data:');
|
|
576
628
|
console.log(chalk_1.default.gray(JSON.stringify(docData, null, 2)));
|
|
@@ -579,7 +631,7 @@ exports.schemaCommand
|
|
|
579
631
|
return;
|
|
580
632
|
}
|
|
581
633
|
// Call the API to push
|
|
582
|
-
const response = await axios_1.default.post(`${
|
|
634
|
+
const response = await axios_1.default.post(`${apiBase}`, {
|
|
583
635
|
action: 'push',
|
|
584
636
|
schema: docData,
|
|
585
637
|
}, {
|
|
@@ -589,12 +641,12 @@ exports.schemaCommand
|
|
|
589
641
|
},
|
|
590
642
|
});
|
|
591
643
|
if (response.data.ok) {
|
|
592
|
-
spinner.succeed(`Schema "${schema.type}" pushed to development`);
|
|
644
|
+
spinner.succeed(`Schema "${schema.type}" pushed to ${env} (development)`);
|
|
593
645
|
console.log('');
|
|
594
646
|
console.log(` Path: ${response.data.path || `araneaSDK/typeSettings/schemas/${schema.type}`}`);
|
|
595
|
-
console.log(` Revision: ${response.data.revisionNumber ||
|
|
647
|
+
console.log(` Revision: ${response.data.revisionNumber || '(new)'}`);
|
|
596
648
|
console.log('');
|
|
597
|
-
console.log(chalk_1.default.cyan(`To promote to production: aranea-sdk schema promote --type ${schema.type}`));
|
|
649
|
+
console.log(chalk_1.default.cyan(`To promote to production: aranea-sdk schema promote --type ${schema.type} --endpoint ${env}`));
|
|
598
650
|
}
|
|
599
651
|
else {
|
|
600
652
|
spinner.fail(`Push failed: ${response.data.error || 'Unknown error'}`);
|
|
@@ -604,6 +656,7 @@ exports.schemaCommand
|
|
|
604
656
|
catch (error) {
|
|
605
657
|
if (error.response?.status === 401) {
|
|
606
658
|
spinner.fail('Authentication failed - invalid or expired token');
|
|
659
|
+
console.log(chalk_1.default.yellow('\nToken is valid for 1 hour. Please refresh your token.'));
|
|
607
660
|
}
|
|
608
661
|
else if (error.response?.status === 403) {
|
|
609
662
|
spinner.fail('Permission denied - insufficient privileges');
|
|
@@ -623,42 +676,67 @@ exports.schemaCommand
|
|
|
623
676
|
.description('Promote a schema from development to production')
|
|
624
677
|
.requiredOption('-t, --type <type>', 'Type name to promote')
|
|
625
678
|
.option('--token <token>', 'Firebase Auth ID token')
|
|
679
|
+
.option('-e, --endpoint <env>', 'Environment (staging|production)', 'staging')
|
|
626
680
|
.option('-d, --dry-run', 'Show what would be promoted without actually promoting')
|
|
681
|
+
.option('-y, --confirm', 'Skip confirmation prompt')
|
|
627
682
|
.action(async (options) => {
|
|
628
|
-
const
|
|
683
|
+
const env = (0, config_1.resolveEnvironment)(options.endpoint);
|
|
684
|
+
const apiBase = getSchemaApiBase(env);
|
|
685
|
+
// Always warn for promote
|
|
686
|
+
console.log(chalk_1.default.yellow(`\nPromoting schema to PRODUCTION state in ${env} environment\n`));
|
|
687
|
+
const spinner = (0, ora_1.default)(`Fetching schema info: ${options.type}...`).start();
|
|
629
688
|
try {
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
console.log(chalk_1.default.yellow('Provide a token using one of:'));
|
|
635
|
-
console.log(' --token <TOKEN>');
|
|
636
|
-
console.log(' export ARANEA_AUTH_TOKEN=<TOKEN>');
|
|
637
|
-
console.log(' export FIREBASE_AUTH_TOKEN=<TOKEN>');
|
|
689
|
+
// First, fetch current schema info
|
|
690
|
+
const schemaResult = await fetchSchema(apiBase, options.type);
|
|
691
|
+
if (!schemaResult.ok) {
|
|
692
|
+
spinner.fail(`Schema "${options.type}" not found`);
|
|
638
693
|
process.exit(1);
|
|
639
694
|
}
|
|
695
|
+
const schema = schemaResult.schema;
|
|
696
|
+
spinner.stop();
|
|
697
|
+
// Show current state
|
|
698
|
+
console.log(chalk_1.default.bold('Current Schema State:'));
|
|
699
|
+
console.log(` Type: ${schema.type}`);
|
|
700
|
+
console.log(` DisplayName: ${schema.displayName}`);
|
|
701
|
+
console.log(` Current State: ${schema.state === 'production' ? chalk_1.default.green(schema.state) : chalk_1.default.yellow(schema.state || 'development')}`);
|
|
702
|
+
console.log(` Revision: ${schema.revisionNumber || schema.version}`);
|
|
703
|
+
console.log(` Environment: ${env}`);
|
|
704
|
+
console.log('');
|
|
705
|
+
if (schema.state === 'production') {
|
|
706
|
+
console.log(chalk_1.default.yellow('Schema is already in production state.'));
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
640
709
|
if (options.dryRun) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
console.log(chalk_1.default.bold('\n=== Dry Run: Schema Promote ===\n'));
|
|
645
|
-
if (!schemaResult.ok) {
|
|
646
|
-
console.log(chalk_1.default.red(`Schema "${options.type}" not found`));
|
|
647
|
-
process.exit(1);
|
|
648
|
-
}
|
|
649
|
-
const schema = schemaResult.schema;
|
|
650
|
-
console.log(chalk_1.default.yellow('Would promote the following schema:'));
|
|
651
|
-
console.log('');
|
|
652
|
-
console.log(` Type: ${schema.type}`);
|
|
653
|
-
console.log(` Current State: ${schema.state || 'unknown'}`);
|
|
654
|
-
console.log(` New State: ${chalk_1.default.green('production')}`);
|
|
655
|
-
console.log(` Revision: ${schema.revisionNumber || schema.version}`);
|
|
710
|
+
console.log(chalk_1.default.bold('=== Dry Run: Would Promote ===\n'));
|
|
711
|
+
console.log(` From: ${chalk_1.default.yellow('development')}`);
|
|
712
|
+
console.log(` To: ${chalk_1.default.green('production')}`);
|
|
656
713
|
console.log('');
|
|
657
714
|
console.log(chalk_1.default.gray('Remove --dry-run to actually promote'));
|
|
658
715
|
return;
|
|
659
716
|
}
|
|
717
|
+
// Confirmation step
|
|
718
|
+
if (!options.confirm) {
|
|
719
|
+
console.log(chalk_1.default.red('This will promote the schema to PRODUCTION state.'));
|
|
720
|
+
console.log(chalk_1.default.red('Existing devices using this schema type will be affected.'));
|
|
721
|
+
console.log('');
|
|
722
|
+
const confirmed = await promptConfirm('Are you sure you want to proceed?');
|
|
723
|
+
if (!confirmed) {
|
|
724
|
+
console.log(chalk_1.default.yellow('\nPromotion cancelled.'));
|
|
725
|
+
return;
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
const token = getAuthToken(options);
|
|
729
|
+
if (!token) {
|
|
730
|
+
console.log(chalk_1.default.red('\nAuthentication required'));
|
|
731
|
+
console.log(chalk_1.default.yellow('Provide a token using one of:'));
|
|
732
|
+
console.log(' --token <TOKEN>');
|
|
733
|
+
console.log(' export ARANEA_AUTH_TOKEN=<TOKEN>');
|
|
734
|
+
console.log(' export FIREBASE_AUTH_TOKEN=<TOKEN>');
|
|
735
|
+
process.exit(1);
|
|
736
|
+
}
|
|
737
|
+
const promoteSpinner = (0, ora_1.default)(`Promoting schema: ${options.type}...`).start();
|
|
660
738
|
// Call the API to promote
|
|
661
|
-
const response = await axios_1.default.post(`${
|
|
739
|
+
const response = await axios_1.default.post(`${apiBase}`, {
|
|
662
740
|
action: 'promote',
|
|
663
741
|
type: options.type,
|
|
664
742
|
}, {
|
|
@@ -668,7 +746,7 @@ exports.schemaCommand
|
|
|
668
746
|
},
|
|
669
747
|
});
|
|
670
748
|
if (response.data.ok) {
|
|
671
|
-
|
|
749
|
+
promoteSpinner.succeed(`Schema "${options.type}" promoted to production`);
|
|
672
750
|
console.log('');
|
|
673
751
|
console.log(` Previous State: ${response.data.previousState || 'development'}`);
|
|
674
752
|
console.log(` New State: ${chalk_1.default.green('production')}`);
|
|
@@ -676,25 +754,26 @@ exports.schemaCommand
|
|
|
676
754
|
console.log('');
|
|
677
755
|
}
|
|
678
756
|
else {
|
|
679
|
-
|
|
757
|
+
promoteSpinner.fail(`Promote failed: ${response.data.error || 'Unknown error'}`);
|
|
680
758
|
process.exit(1);
|
|
681
759
|
}
|
|
682
760
|
}
|
|
683
761
|
catch (error) {
|
|
684
762
|
if (error.response?.status === 401) {
|
|
685
|
-
|
|
763
|
+
console.log(chalk_1.default.red('\nAuthentication failed - invalid or expired token'));
|
|
764
|
+
console.log(chalk_1.default.yellow('Token is valid for 1 hour. Please refresh your token.'));
|
|
686
765
|
}
|
|
687
766
|
else if (error.response?.status === 403) {
|
|
688
|
-
|
|
767
|
+
console.log(chalk_1.default.red('\nPermission denied - insufficient privileges'));
|
|
689
768
|
}
|
|
690
769
|
else if (error.response?.status === 404) {
|
|
691
|
-
|
|
770
|
+
console.log(chalk_1.default.red(`\nSchema "${options.type}" not found`));
|
|
692
771
|
}
|
|
693
772
|
else if (error.response?.data?.error) {
|
|
694
|
-
|
|
773
|
+
console.log(chalk_1.default.red(`\nPromote failed: ${error.response.data.error}`));
|
|
695
774
|
}
|
|
696
775
|
else {
|
|
697
|
-
|
|
776
|
+
console.log(chalk_1.default.red(`\nPromote failed: ${error.message}`));
|
|
698
777
|
}
|
|
699
778
|
process.exit(1);
|
|
700
779
|
}
|
|
@@ -704,12 +783,15 @@ exports.schemaCommand
|
|
|
704
783
|
.command('info')
|
|
705
784
|
.description('Show detailed schema information with revision history')
|
|
706
785
|
.requiredOption('-t, --type <type>', 'Type name')
|
|
786
|
+
.option('-e, --endpoint <env>', 'Environment (staging|production)', 'staging')
|
|
707
787
|
.option('--json', 'Output as JSON')
|
|
708
788
|
.action(async (options) => {
|
|
709
|
-
const
|
|
789
|
+
const env = (0, config_1.resolveEnvironment)(options.endpoint);
|
|
790
|
+
const apiBase = getSchemaApiBase(env);
|
|
791
|
+
const spinner = (0, ora_1.default)(`Fetching schema info: ${options.type} from ${env}...`).start();
|
|
710
792
|
try {
|
|
711
793
|
// Fetch schema from API with extended info
|
|
712
|
-
const response = await axios_1.default.get(`${
|
|
794
|
+
const response = await axios_1.default.get(`${apiBase}`, {
|
|
713
795
|
params: {
|
|
714
796
|
action: 'info',
|
|
715
797
|
type: options.type,
|
|
@@ -728,10 +810,10 @@ exports.schemaCommand
|
|
|
728
810
|
const schema = response.data.schema;
|
|
729
811
|
const revisions = response.data.revisions || [];
|
|
730
812
|
if (options.json) {
|
|
731
|
-
console.log(JSON.stringify({ schema, revisions }, null, 2));
|
|
813
|
+
console.log(JSON.stringify({ schema, revisions, environment: env }, null, 2));
|
|
732
814
|
return;
|
|
733
815
|
}
|
|
734
|
-
console.log(chalk_1.default.bold(`\n=== Schema Info: ${schema.type} ===\n`));
|
|
816
|
+
console.log(chalk_1.default.bold(`\n=== Schema Info: ${schema.type} (${env}) ===\n`));
|
|
735
817
|
// Basic info
|
|
736
818
|
console.log(chalk_1.default.cyan('Basic Information:'));
|
|
737
819
|
console.log(` Type: ${schema.type}`);
|
|
@@ -778,9 +860,9 @@ exports.schemaCommand
|
|
|
778
860
|
// Actions
|
|
779
861
|
console.log(chalk_1.default.gray('Actions:'));
|
|
780
862
|
if (schema.state !== 'production') {
|
|
781
|
-
console.log(chalk_1.default.gray(` Promote: aranea-sdk schema promote --type ${schema.type}`));
|
|
863
|
+
console.log(chalk_1.default.gray(` Promote: aranea-sdk schema promote --type ${schema.type} --endpoint ${env}`));
|
|
782
864
|
}
|
|
783
|
-
console.log(chalk_1.default.gray(` Get full schema: aranea-sdk schema get --type ${schema.type} --json`));
|
|
865
|
+
console.log(chalk_1.default.gray(` Get full schema: aranea-sdk schema get --type ${schema.type} --endpoint ${env} --json`));
|
|
784
866
|
console.log('');
|
|
785
867
|
}
|
|
786
868
|
catch (error) {
|
package/dist/config.d.ts
CHANGED
|
@@ -1,17 +1,52 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* AraneaSDK CLI Configuration
|
|
3
|
+
*
|
|
4
|
+
* Environment switching:
|
|
5
|
+
* - ARANEA_ENV environment variable (production | staging)
|
|
6
|
+
* - --endpoint option on commands
|
|
7
|
+
* - Default: staging (safety first)
|
|
3
8
|
*/
|
|
9
|
+
export type Environment = 'production' | 'staging';
|
|
4
10
|
export interface Endpoint {
|
|
5
11
|
gate: string;
|
|
6
12
|
state: string;
|
|
7
13
|
command: string;
|
|
8
14
|
mqtt?: string;
|
|
15
|
+
schemaAPI: string;
|
|
16
|
+
knowledgeAPI: string;
|
|
17
|
+
metatronAPI: string;
|
|
18
|
+
webhookBase: string;
|
|
9
19
|
}
|
|
10
|
-
export declare const ENDPOINTS: Record<
|
|
20
|
+
export declare const ENDPOINTS: Record<Environment, Endpoint>;
|
|
11
21
|
export declare const TEST_TENANT: {
|
|
12
22
|
tid: string;
|
|
13
23
|
primaryLacisId: string;
|
|
14
24
|
primaryEmail: string;
|
|
15
25
|
primaryCic: string;
|
|
16
26
|
};
|
|
27
|
+
/**
|
|
28
|
+
* Get current environment from ARANEA_ENV or default to staging
|
|
29
|
+
*/
|
|
30
|
+
export declare function getCurrentEnvironment(): Environment;
|
|
31
|
+
/**
|
|
32
|
+
* Resolve environment from option or environment variable
|
|
33
|
+
* @param optionValue - Value from --endpoint option
|
|
34
|
+
* @returns Resolved environment
|
|
35
|
+
*/
|
|
36
|
+
export declare function resolveEnvironment(optionValue?: string): Environment;
|
|
37
|
+
/**
|
|
38
|
+
* Get endpoint configuration for environment
|
|
39
|
+
*/
|
|
17
40
|
export declare function getEndpoint(env: string): Endpoint;
|
|
41
|
+
/**
|
|
42
|
+
* CelestialGlobe webhook configuration
|
|
43
|
+
*/
|
|
44
|
+
export interface WebhookConfig {
|
|
45
|
+
ingestUrl: string;
|
|
46
|
+
stateReportUrl: string;
|
|
47
|
+
}
|
|
48
|
+
export declare function getWebhookConfig(env: Environment, fid: string): WebhookConfig;
|
|
49
|
+
/**
|
|
50
|
+
* Display environment warning for production operations
|
|
51
|
+
*/
|
|
52
|
+
export declare function warnIfProduction(env: Environment, operation: string): void;
|
package/dist/config.js
CHANGED
|
@@ -1,22 +1,39 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
3
|
* AraneaSDK CLI Configuration
|
|
4
|
+
*
|
|
5
|
+
* Environment switching:
|
|
6
|
+
* - ARANEA_ENV environment variable (production | staging)
|
|
7
|
+
* - --endpoint option on commands
|
|
8
|
+
* - Default: staging (safety first)
|
|
4
9
|
*/
|
|
5
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
11
|
exports.TEST_TENANT = exports.ENDPOINTS = void 0;
|
|
12
|
+
exports.getCurrentEnvironment = getCurrentEnvironment;
|
|
13
|
+
exports.resolveEnvironment = resolveEnvironment;
|
|
7
14
|
exports.getEndpoint = getEndpoint;
|
|
15
|
+
exports.getWebhookConfig = getWebhookConfig;
|
|
16
|
+
exports.warnIfProduction = warnIfProduction;
|
|
8
17
|
exports.ENDPOINTS = {
|
|
9
18
|
production: {
|
|
10
19
|
gate: 'https://asia-northeast1-mobesorder.cloudfunctions.net/araneaDeviceGate',
|
|
11
20
|
state: 'https://asia-northeast1-mobesorder.cloudfunctions.net/deviceStateReport',
|
|
12
21
|
command: 'https://asia-northeast1-mobesorder.cloudfunctions.net/araneaDeviceCommand',
|
|
13
22
|
mqtt: 'wss://aranea-mqtt-bridge-1010551946141.asia-northeast1.run.app',
|
|
23
|
+
schemaAPI: 'https://asia-northeast1-mobesorder.cloudfunctions.net/araneaSchemaAPI',
|
|
24
|
+
knowledgeAPI: 'https://asia-northeast1-mobesorder.cloudfunctions.net/araneaKnowledgeManagement',
|
|
25
|
+
metatronAPI: 'https://asia-northeast1-mobesorder.cloudfunctions.net/araneaMetatronQuery',
|
|
26
|
+
webhookBase: 'https://us-central1-mobesorder.cloudfunctions.net',
|
|
14
27
|
},
|
|
15
28
|
staging: {
|
|
16
29
|
gate: 'https://asia-northeast1-mobesorder-staging.cloudfunctions.net/araneaDeviceGate',
|
|
17
30
|
state: 'https://asia-northeast1-mobesorder-staging.cloudfunctions.net/deviceStateReport',
|
|
18
31
|
command: 'https://asia-northeast1-mobesorder-staging.cloudfunctions.net/araneaDeviceCommand',
|
|
19
32
|
mqtt: 'wss://aranea-mqtt-bridge-staging.asia-northeast1.run.app',
|
|
33
|
+
schemaAPI: 'https://asia-northeast1-mobesorder-staging.cloudfunctions.net/araneaSchemaAPI',
|
|
34
|
+
knowledgeAPI: 'https://asia-northeast1-mobesorder-staging.cloudfunctions.net/araneaKnowledgeManagement',
|
|
35
|
+
metatronAPI: 'https://asia-northeast1-mobesorder-staging.cloudfunctions.net/araneaMetatronQuery',
|
|
36
|
+
webhookBase: 'https://us-central1-mobesorder-staging.cloudfunctions.net',
|
|
20
37
|
},
|
|
21
38
|
};
|
|
22
39
|
exports.TEST_TENANT = {
|
|
@@ -25,10 +42,60 @@ exports.TEST_TENANT = {
|
|
|
25
42
|
primaryEmail: 'dev@araneadevice.dev',
|
|
26
43
|
primaryCic: '022029',
|
|
27
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Get current environment from ARANEA_ENV or default to staging
|
|
47
|
+
*/
|
|
48
|
+
function getCurrentEnvironment() {
|
|
49
|
+
const env = process.env.ARANEA_ENV?.toLowerCase();
|
|
50
|
+
if (env === 'production' || env === 'prod') {
|
|
51
|
+
return 'production';
|
|
52
|
+
}
|
|
53
|
+
// Default to staging for safety
|
|
54
|
+
return 'staging';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Resolve environment from option or environment variable
|
|
58
|
+
* @param optionValue - Value from --endpoint option
|
|
59
|
+
* @returns Resolved environment
|
|
60
|
+
*/
|
|
61
|
+
function resolveEnvironment(optionValue) {
|
|
62
|
+
if (optionValue) {
|
|
63
|
+
const normalized = optionValue.toLowerCase();
|
|
64
|
+
if (normalized === 'production' || normalized === 'prod') {
|
|
65
|
+
return 'production';
|
|
66
|
+
}
|
|
67
|
+
if (normalized === 'staging' || normalized === 'stg') {
|
|
68
|
+
return 'staging';
|
|
69
|
+
}
|
|
70
|
+
throw new Error(`Unknown environment: ${optionValue}. Use 'production' or 'staging'.`);
|
|
71
|
+
}
|
|
72
|
+
return getCurrentEnvironment();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Get endpoint configuration for environment
|
|
76
|
+
*/
|
|
28
77
|
function getEndpoint(env) {
|
|
78
|
+
const normalized = env.toLowerCase();
|
|
79
|
+
if (normalized === 'production' || normalized === 'prod') {
|
|
80
|
+
return exports.ENDPOINTS.production;
|
|
81
|
+
}
|
|
82
|
+
if (normalized === 'staging' || normalized === 'stg') {
|
|
83
|
+
return exports.ENDPOINTS.staging;
|
|
84
|
+
}
|
|
85
|
+
throw new Error(`Unknown environment: ${env}. Use 'production' or 'staging'.`);
|
|
86
|
+
}
|
|
87
|
+
function getWebhookConfig(env, fid) {
|
|
29
88
|
const endpoint = exports.ENDPOINTS[env];
|
|
30
|
-
|
|
31
|
-
|
|
89
|
+
return {
|
|
90
|
+
ingestUrl: `${endpoint.webhookBase}/celestialGlobe_ingest?fid=${fid}&source=omada`,
|
|
91
|
+
stateReportUrl: `${endpoint.webhookBase}/deviceStateReport`,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Display environment warning for production operations
|
|
96
|
+
*/
|
|
97
|
+
function warnIfProduction(env, operation) {
|
|
98
|
+
if (env === 'production') {
|
|
99
|
+
console.warn(`\x1b[33m⚠️ WARNING: ${operation} targeting PRODUCTION environment\x1b[0m`);
|
|
32
100
|
}
|
|
33
|
-
return endpoint;
|
|
34
101
|
}
|
package/dist/index.js
CHANGED
|
@@ -23,11 +23,12 @@ const register_1 = require("./commands/register");
|
|
|
23
23
|
const mqtt_1 = require("./commands/mqtt");
|
|
24
24
|
const knowledge_1 = require("./commands/knowledge");
|
|
25
25
|
const metatron_1 = require("./commands/metatron");
|
|
26
|
+
const auth_1 = require("./commands/auth");
|
|
26
27
|
const program = new commander_1.Command();
|
|
27
28
|
program
|
|
28
29
|
.name('aranea-sdk')
|
|
29
30
|
.description('AraneaSDK CLI - デバイス開発支援ツール')
|
|
30
|
-
.version('0.3.
|
|
31
|
+
.version('0.3.1');
|
|
31
32
|
// test コマンド
|
|
32
33
|
program.addCommand(test_1.testCommand);
|
|
33
34
|
// simulate コマンド
|
|
@@ -44,4 +45,6 @@ program.addCommand(mqtt_1.mqttCommand);
|
|
|
44
45
|
program.addCommand(knowledge_1.knowledgeCommand);
|
|
45
46
|
// metatron コマンド (v0.2.1)
|
|
46
47
|
program.addCommand(metatron_1.metatronCommand);
|
|
48
|
+
// auth コマンド (v0.3.1)
|
|
49
|
+
program.addCommand(auth_1.authCommand);
|
|
47
50
|
program.parse(process.argv);
|