@lanonasis/cli 3.9.4 → 3.9.6
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/CHANGELOG.md +15 -0
- package/README.md +46 -15
- package/dist/commands/config.js +11 -0
- package/dist/commands/memory.js +276 -6
- package/dist/index-simple.js +29 -4
- package/dist/index.js +32 -4
- package/dist/mcp/schemas/tool-schemas.d.ts +24 -24
- package/dist/utils/api.d.ts +6 -0
- package/dist/utils/api.js +264 -14
- package/dist/utils/config.d.ts +11 -0
- package/dist/utils/config.js +298 -33
- package/dist/ux/implementations/TextInputHandlerImpl.d.ts +1 -0
- package/dist/ux/implementations/TextInputHandlerImpl.js +8 -3
- package/package.json +5 -5
package/dist/utils/config.js
CHANGED
|
@@ -434,6 +434,200 @@ export class CLIConfig {
|
|
|
434
434
|
}
|
|
435
435
|
throw new Error('Auth health endpoints unreachable');
|
|
436
436
|
}
|
|
437
|
+
getAuthVerificationEndpoints(pathname) {
|
|
438
|
+
const authBase = (this.config.discoveredServices?.auth_base || 'https://auth.lanonasis.com').replace(/\/$/, '');
|
|
439
|
+
return Array.from(new Set([
|
|
440
|
+
`${authBase}${pathname}`,
|
|
441
|
+
`https://auth.lanonasis.com${pathname}`,
|
|
442
|
+
`http://localhost:4000${pathname}`
|
|
443
|
+
]));
|
|
444
|
+
}
|
|
445
|
+
extractAuthErrorMessage(payload) {
|
|
446
|
+
if (!payload || typeof payload !== 'object') {
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
const data = payload;
|
|
450
|
+
const fields = ['message', 'error', 'reason', 'code'];
|
|
451
|
+
for (const field of fields) {
|
|
452
|
+
const value = data[field];
|
|
453
|
+
if (typeof value === 'string' && value.trim().length > 0) {
|
|
454
|
+
return value;
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
return undefined;
|
|
458
|
+
}
|
|
459
|
+
async verifyTokenWithAuthGateway(token) {
|
|
460
|
+
const headers = {
|
|
461
|
+
'Authorization': `Bearer ${token}`,
|
|
462
|
+
'X-Project-Scope': 'lanonasis-maas'
|
|
463
|
+
};
|
|
464
|
+
let fallbackReason = 'Unable to verify token with auth gateway';
|
|
465
|
+
// Primary check (required by auth contract): /v1/auth/verify
|
|
466
|
+
for (const endpoint of this.getAuthVerificationEndpoints('/v1/auth/verify')) {
|
|
467
|
+
try {
|
|
468
|
+
const response = await axios.post(endpoint, {}, {
|
|
469
|
+
headers,
|
|
470
|
+
timeout: 5000,
|
|
471
|
+
proxy: false
|
|
472
|
+
});
|
|
473
|
+
const payload = response.data;
|
|
474
|
+
if (payload.valid === true || Boolean(payload.payload)) {
|
|
475
|
+
return { valid: true, method: 'token', endpoint };
|
|
476
|
+
}
|
|
477
|
+
if (payload.valid === false) {
|
|
478
|
+
return {
|
|
479
|
+
valid: false,
|
|
480
|
+
method: 'token',
|
|
481
|
+
endpoint,
|
|
482
|
+
reason: this.extractAuthErrorMessage(payload) || 'Token is invalid'
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
const normalizedError = this.normalizeServiceError(error);
|
|
488
|
+
const responsePayload = normalizedError.response?.data;
|
|
489
|
+
const responseCode = typeof responsePayload?.code === 'string' ? responsePayload.code : undefined;
|
|
490
|
+
const reason = this.extractAuthErrorMessage(responsePayload) || normalizedError.message || fallbackReason;
|
|
491
|
+
fallbackReason = reason;
|
|
492
|
+
// If auth gateway explicitly rejected token, stop early.
|
|
493
|
+
if ((normalizedError.response?.status === 401 || normalizedError.response?.status === 403) &&
|
|
494
|
+
responseCode &&
|
|
495
|
+
responseCode !== 'AUTH_TOKEN_MISSING') {
|
|
496
|
+
return { valid: false, method: 'token', endpoint, reason };
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
// Fallback for deployments where proxy layers strip Authorization headers.
|
|
501
|
+
for (const endpoint of this.getAuthVerificationEndpoints('/v1/auth/verify-token')) {
|
|
502
|
+
try {
|
|
503
|
+
const response = await axios.post(endpoint, { token }, {
|
|
504
|
+
headers: {
|
|
505
|
+
'Content-Type': 'application/json',
|
|
506
|
+
'X-Project-Scope': 'lanonasis-maas'
|
|
507
|
+
},
|
|
508
|
+
timeout: 5000,
|
|
509
|
+
proxy: false
|
|
510
|
+
});
|
|
511
|
+
const payload = response.data;
|
|
512
|
+
if (payload.valid === true) {
|
|
513
|
+
return { valid: true, method: 'token', endpoint };
|
|
514
|
+
}
|
|
515
|
+
if (payload.valid === false) {
|
|
516
|
+
return {
|
|
517
|
+
valid: false,
|
|
518
|
+
method: 'token',
|
|
519
|
+
endpoint,
|
|
520
|
+
reason: this.extractAuthErrorMessage(payload) || 'Token is invalid'
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
catch (error) {
|
|
525
|
+
const normalizedError = this.normalizeServiceError(error);
|
|
526
|
+
const responsePayload = normalizedError.response?.data;
|
|
527
|
+
fallbackReason = this.extractAuthErrorMessage(responsePayload) || normalizedError.message || fallbackReason;
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
return {
|
|
531
|
+
valid: false,
|
|
532
|
+
method: 'token',
|
|
533
|
+
reason: fallbackReason
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
async verifyVendorKeyWithAuthGateway(vendorKey) {
|
|
537
|
+
const headers = {
|
|
538
|
+
'X-API-Key': vendorKey,
|
|
539
|
+
'X-Auth-Method': 'vendor_key',
|
|
540
|
+
'X-Project-Scope': 'lanonasis-maas'
|
|
541
|
+
};
|
|
542
|
+
let fallbackReason = 'Unable to verify API key with auth gateway';
|
|
543
|
+
// Primary check (required by auth contract): /v1/auth/verify
|
|
544
|
+
for (const endpoint of this.getAuthVerificationEndpoints('/v1/auth/verify')) {
|
|
545
|
+
try {
|
|
546
|
+
const response = await axios.post(endpoint, {}, {
|
|
547
|
+
headers,
|
|
548
|
+
timeout: 5000,
|
|
549
|
+
proxy: false
|
|
550
|
+
});
|
|
551
|
+
const payload = response.data;
|
|
552
|
+
if (payload.valid === true || Boolean(payload.payload)) {
|
|
553
|
+
return { valid: true, method: 'vendor_key', endpoint };
|
|
554
|
+
}
|
|
555
|
+
if (payload.valid === false) {
|
|
556
|
+
return {
|
|
557
|
+
valid: false,
|
|
558
|
+
method: 'vendor_key',
|
|
559
|
+
endpoint,
|
|
560
|
+
reason: this.extractAuthErrorMessage(payload) || 'API key is invalid'
|
|
561
|
+
};
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
catch (error) {
|
|
565
|
+
const normalizedError = this.normalizeServiceError(error);
|
|
566
|
+
const responsePayload = normalizedError.response?.data;
|
|
567
|
+
const responseCode = typeof responsePayload?.code === 'string' ? responsePayload.code : undefined;
|
|
568
|
+
const reason = this.extractAuthErrorMessage(responsePayload) || normalizedError.message || fallbackReason;
|
|
569
|
+
fallbackReason = reason;
|
|
570
|
+
// If auth gateway explicitly rejected API key, stop early.
|
|
571
|
+
if ((normalizedError.response?.status === 401 || normalizedError.response?.status === 403) &&
|
|
572
|
+
responseCode &&
|
|
573
|
+
responseCode !== 'AUTH_TOKEN_MISSING') {
|
|
574
|
+
return { valid: false, method: 'vendor_key', endpoint, reason };
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
// Fallback for deployments where reverse proxies don't forward custom auth headers on /verify.
|
|
579
|
+
for (const endpoint of this.getAuthVerificationEndpoints('/v1/auth/verify-api-key')) {
|
|
580
|
+
try {
|
|
581
|
+
const response = await axios.post(endpoint, {}, {
|
|
582
|
+
headers,
|
|
583
|
+
timeout: 5000,
|
|
584
|
+
proxy: false
|
|
585
|
+
});
|
|
586
|
+
const payload = response.data;
|
|
587
|
+
if (payload.valid === true) {
|
|
588
|
+
return { valid: true, method: 'vendor_key', endpoint };
|
|
589
|
+
}
|
|
590
|
+
if (payload.valid === false) {
|
|
591
|
+
return {
|
|
592
|
+
valid: false,
|
|
593
|
+
method: 'vendor_key',
|
|
594
|
+
endpoint,
|
|
595
|
+
reason: this.extractAuthErrorMessage(payload) || 'API key is invalid'
|
|
596
|
+
};
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
const normalizedError = this.normalizeServiceError(error);
|
|
601
|
+
const responsePayload = normalizedError.response?.data;
|
|
602
|
+
fallbackReason = this.extractAuthErrorMessage(responsePayload) || normalizedError.message || fallbackReason;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
return {
|
|
606
|
+
valid: false,
|
|
607
|
+
method: 'vendor_key',
|
|
608
|
+
reason: fallbackReason
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
async verifyCurrentCredentialsWithServer() {
|
|
612
|
+
await this.refreshTokenIfNeeded();
|
|
613
|
+
await this.discoverServices();
|
|
614
|
+
const token = this.getToken();
|
|
615
|
+
const vendorKey = await this.getVendorKeyAsync();
|
|
616
|
+
if (this.config.authMethod === 'vendor_key' && vendorKey) {
|
|
617
|
+
return this.verifyVendorKeyWithAuthGateway(vendorKey);
|
|
618
|
+
}
|
|
619
|
+
if (token) {
|
|
620
|
+
return this.verifyTokenWithAuthGateway(token);
|
|
621
|
+
}
|
|
622
|
+
if (vendorKey) {
|
|
623
|
+
return this.verifyVendorKeyWithAuthGateway(vendorKey);
|
|
624
|
+
}
|
|
625
|
+
return {
|
|
626
|
+
valid: false,
|
|
627
|
+
method: 'none',
|
|
628
|
+
reason: 'No credentials configured'
|
|
629
|
+
};
|
|
630
|
+
}
|
|
437
631
|
// Manual endpoint override functionality
|
|
438
632
|
async setManualEndpoints(endpoints) {
|
|
439
633
|
if (!this.config.discoveredServices) {
|
|
@@ -520,16 +714,12 @@ export class CLIConfig {
|
|
|
520
714
|
return;
|
|
521
715
|
}
|
|
522
716
|
try {
|
|
523
|
-
// Import axios dynamically to avoid circular dependency
|
|
524
|
-
// Ensure service discovery is done
|
|
525
717
|
await this.discoverServices();
|
|
526
|
-
const
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
'X-Project-Scope': 'lanonasis-maas'
|
|
532
|
-
}, { timeout: 10000, proxy: false });
|
|
718
|
+
const verification = await this.verifyVendorKeyWithAuthGateway(vendorKey);
|
|
719
|
+
if (verification.valid) {
|
|
720
|
+
return;
|
|
721
|
+
}
|
|
722
|
+
throw new Error(verification.reason || 'Authentication failed. The key may be invalid, expired, or revoked.');
|
|
533
723
|
}
|
|
534
724
|
catch (error) {
|
|
535
725
|
const normalizedError = this.normalizeServiceError(error);
|
|
@@ -678,6 +868,9 @@ export class CLIConfig {
|
|
|
678
868
|
return this.config.user;
|
|
679
869
|
}
|
|
680
870
|
async isAuthenticated() {
|
|
871
|
+
// Attempt refresh for OAuth sessions before checks (prevents intermittent auth dropouts).
|
|
872
|
+
// This is safe to call even when not using OAuth; it will no-op.
|
|
873
|
+
await this.refreshTokenIfNeeded();
|
|
681
874
|
// Check if using vendor key authentication
|
|
682
875
|
if (this.config.authMethod === 'vendor_key') {
|
|
683
876
|
// Use async method to read from encrypted ApiKeyStorage
|
|
@@ -698,21 +891,17 @@ export class CLIConfig {
|
|
|
698
891
|
}
|
|
699
892
|
// Vendor key not recently validated - verify with server
|
|
700
893
|
try {
|
|
701
|
-
await this.
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
'X-API-Key': vendorKey,
|
|
706
|
-
'X-Auth-Method': 'vendor_key',
|
|
707
|
-
'X-Project-Scope': 'lanonasis-maas'
|
|
708
|
-
}, { timeout: 5000, proxy: false });
|
|
894
|
+
const verification = await this.verifyVendorKeyWithAuthGateway(vendorKey);
|
|
895
|
+
if (!verification.valid) {
|
|
896
|
+
throw new Error(verification.reason || 'Vendor key validation failed');
|
|
897
|
+
}
|
|
709
898
|
// Update last validated timestamp on success
|
|
710
899
|
this.config.lastValidated = new Date().toISOString();
|
|
711
900
|
await this.save().catch(() => { }); // Don't fail auth check if save fails
|
|
712
901
|
this.authCheckCache = { isValid: true, timestamp: Date.now() };
|
|
713
902
|
return true;
|
|
714
903
|
}
|
|
715
|
-
catch
|
|
904
|
+
catch {
|
|
716
905
|
// Server validation failed - check for grace period (7 days offline)
|
|
717
906
|
const gracePeriod = 7 * 24 * 60 * 60 * 1000;
|
|
718
907
|
const withinGracePeriod = lastValidated &&
|
|
@@ -736,6 +925,16 @@ export class CLIConfig {
|
|
|
736
925
|
const token = this.getToken();
|
|
737
926
|
if (!token)
|
|
738
927
|
return false;
|
|
928
|
+
// OAuth tokens are often opaque (not JWT). Prefer local expiry metadata when present.
|
|
929
|
+
if (this.config.authMethod === 'oauth') {
|
|
930
|
+
const tokenExpiresAt = this.get('token_expires_at');
|
|
931
|
+
if (typeof tokenExpiresAt === 'number') {
|
|
932
|
+
const isValid = Date.now() < tokenExpiresAt;
|
|
933
|
+
this.authCheckCache = { isValid, timestamp: Date.now() };
|
|
934
|
+
return isValid;
|
|
935
|
+
}
|
|
936
|
+
// Fall through to legacy validation when we don't have expiry metadata.
|
|
937
|
+
}
|
|
739
938
|
// Check cache first
|
|
740
939
|
if (this.authCheckCache && (Date.now() - this.authCheckCache.timestamp) < this.AUTH_CACHE_TTL) {
|
|
741
940
|
return this.authCheckCache.isValid;
|
|
@@ -934,22 +1133,14 @@ export class CLIConfig {
|
|
|
934
1133
|
if (!vendorKey && !token) {
|
|
935
1134
|
return false;
|
|
936
1135
|
}
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
if (vendorKey) {
|
|
945
|
-
headers['X-API-Key'] = vendorKey;
|
|
946
|
-
headers['X-Auth-Method'] = 'vendor_key';
|
|
947
|
-
}
|
|
948
|
-
else if (token) {
|
|
949
|
-
headers['Authorization'] = `Bearer ${token}`;
|
|
950
|
-
headers['X-Auth-Method'] = 'jwt';
|
|
1136
|
+
const verification = this.config.authMethod === 'vendor_key' && vendorKey
|
|
1137
|
+
? await this.verifyVendorKeyWithAuthGateway(vendorKey)
|
|
1138
|
+
: token
|
|
1139
|
+
? await this.verifyTokenWithAuthGateway(token)
|
|
1140
|
+
: await this.verifyVendorKeyWithAuthGateway(vendorKey);
|
|
1141
|
+
if (!verification.valid) {
|
|
1142
|
+
throw new Error(verification.reason || 'Stored credentials are invalid');
|
|
951
1143
|
}
|
|
952
|
-
await this.pingAuthHealth(axios, authBase, headers);
|
|
953
1144
|
// Update last validated timestamp
|
|
954
1145
|
this.config.lastValidated = new Date().toISOString();
|
|
955
1146
|
await this.resetFailureCount();
|
|
@@ -968,11 +1159,85 @@ export class CLIConfig {
|
|
|
968
1159
|
return;
|
|
969
1160
|
}
|
|
970
1161
|
try {
|
|
1162
|
+
// OAuth token refresh (opaque tokens + refresh_token + token_expires_at)
|
|
1163
|
+
if (this.config.authMethod === 'oauth') {
|
|
1164
|
+
const refreshToken = this.get('refresh_token');
|
|
1165
|
+
if (!refreshToken) {
|
|
1166
|
+
return;
|
|
1167
|
+
}
|
|
1168
|
+
const tokenExpiresAtRaw = this.get('token_expires_at');
|
|
1169
|
+
const tokenExpiresAt = (() => {
|
|
1170
|
+
const n = typeof tokenExpiresAtRaw === 'number'
|
|
1171
|
+
? tokenExpiresAtRaw
|
|
1172
|
+
: typeof tokenExpiresAtRaw === 'string'
|
|
1173
|
+
? Number(tokenExpiresAtRaw)
|
|
1174
|
+
: undefined;
|
|
1175
|
+
if (typeof n !== 'number' || !Number.isFinite(n) || n <= 0) {
|
|
1176
|
+
return undefined;
|
|
1177
|
+
}
|
|
1178
|
+
// Support both seconds and milliseconds since epoch.
|
|
1179
|
+
// Seconds are ~1.7e9; ms are ~1.7e12.
|
|
1180
|
+
return n < 1e11 ? n * 1000 : n;
|
|
1181
|
+
})();
|
|
1182
|
+
const nowMs = Date.now();
|
|
1183
|
+
const refreshWindowMs = 5 * 60 * 1000; // 5 minutes
|
|
1184
|
+
// If we don't know expiry, don't force a refresh.
|
|
1185
|
+
if (typeof tokenExpiresAt !== 'number' || nowMs < (tokenExpiresAt - refreshWindowMs)) {
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
await this.discoverServices();
|
|
1189
|
+
const authBase = this.getDiscoveredApiUrl();
|
|
1190
|
+
const resp = await axios.post(`${authBase}/oauth/token`, {
|
|
1191
|
+
grant_type: 'refresh_token',
|
|
1192
|
+
refresh_token: refreshToken,
|
|
1193
|
+
client_id: 'lanonasis-cli'
|
|
1194
|
+
}, {
|
|
1195
|
+
headers: { 'Content-Type': 'application/json' },
|
|
1196
|
+
timeout: 10000,
|
|
1197
|
+
proxy: false
|
|
1198
|
+
});
|
|
1199
|
+
// Some gateways wrap responses as `{ data: { ... } }`.
|
|
1200
|
+
const raw = resp?.data;
|
|
1201
|
+
const payload = raw && typeof raw === 'object' && raw.data && typeof raw.data === 'object'
|
|
1202
|
+
? raw.data
|
|
1203
|
+
: raw;
|
|
1204
|
+
const accessToken = payload?.access_token ?? payload?.token;
|
|
1205
|
+
const refreshedRefreshToken = payload?.refresh_token;
|
|
1206
|
+
const expiresIn = payload?.expires_in;
|
|
1207
|
+
if (typeof accessToken !== 'string' || accessToken.length === 0) {
|
|
1208
|
+
throw new Error('Token refresh response missing access_token');
|
|
1209
|
+
}
|
|
1210
|
+
// setToken() assumes JWT by default; ensure authMethod stays oauth after storing.
|
|
1211
|
+
await this.setToken(accessToken);
|
|
1212
|
+
this.config.authMethod = 'oauth';
|
|
1213
|
+
if (typeof refreshedRefreshToken === 'string' && refreshedRefreshToken.length > 0) {
|
|
1214
|
+
this.config.refresh_token = refreshedRefreshToken;
|
|
1215
|
+
}
|
|
1216
|
+
if (typeof expiresIn === 'number' && Number.isFinite(expiresIn)) {
|
|
1217
|
+
this.config.token_expires_at = Date.now() + (expiresIn * 1000);
|
|
1218
|
+
}
|
|
1219
|
+
// Keep the encrypted "vendor key" in sync for MCP/WebSocket clients that use X-API-Key.
|
|
1220
|
+
// This does not change authMethod away from oauth (setVendorKey guards against that).
|
|
1221
|
+
try {
|
|
1222
|
+
await this.setVendorKey(accessToken);
|
|
1223
|
+
}
|
|
1224
|
+
catch {
|
|
1225
|
+
// Non-fatal: bearer token refresh still helps API calls.
|
|
1226
|
+
}
|
|
1227
|
+
await this.save().catch(() => { });
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
971
1230
|
// Check if token is JWT and if it's close to expiry
|
|
972
1231
|
if (token.startsWith('cli_')) {
|
|
973
1232
|
// CLI tokens don't need refresh, they're long-lived
|
|
974
1233
|
return;
|
|
975
1234
|
}
|
|
1235
|
+
// Only attempt JWT refresh for tokens that look like JWTs.
|
|
1236
|
+
// OAuth access tokens in this system can be opaque strings; treating them as JWTs
|
|
1237
|
+
// creates noisy failures and can cause unwanted state writes.
|
|
1238
|
+
if (token.split('.').length !== 3) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
976
1241
|
const decoded = jwtDecode(token);
|
|
977
1242
|
const now = Date.now() / 1000;
|
|
978
1243
|
const exp = typeof decoded.exp === 'number' ? decoded.exp : 0;
|
|
@@ -152,7 +152,7 @@ export class TextInputHandlerImpl {
|
|
|
152
152
|
};
|
|
153
153
|
// Ensure stdin is flowing before adding listener
|
|
154
154
|
// This is critical after inquirer prompts which may pause stdin
|
|
155
|
-
|
|
155
|
+
this.resumeStdinIfSupported();
|
|
156
156
|
process.stdin.on('data', handleKeypress);
|
|
157
157
|
}
|
|
158
158
|
catch (error) {
|
|
@@ -168,13 +168,13 @@ export class TextInputHandlerImpl {
|
|
|
168
168
|
if (!this.isRawModeEnabled && process.stdin.isTTY) {
|
|
169
169
|
this.originalStdinMode = process.stdin.isRaw;
|
|
170
170
|
process.stdin.setRawMode(true);
|
|
171
|
-
|
|
171
|
+
this.resumeStdinIfSupported(); // Ensure stdin is flowing to receive data events
|
|
172
172
|
this.isRawModeEnabled = true;
|
|
173
173
|
}
|
|
174
174
|
else if (!process.stdin.isTTY) {
|
|
175
175
|
// Non-TTY mode - can't use raw mode, fall back to line mode
|
|
176
176
|
console.error('Warning: Not a TTY, inline text input may not work correctly');
|
|
177
|
-
|
|
177
|
+
this.resumeStdinIfSupported();
|
|
178
178
|
}
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
@@ -302,6 +302,11 @@ export class TextInputHandlerImpl {
|
|
|
302
302
|
}
|
|
303
303
|
return key;
|
|
304
304
|
}
|
|
305
|
+
resumeStdinIfSupported() {
|
|
306
|
+
if (typeof process.stdin.resume === 'function') {
|
|
307
|
+
process.stdin.resume();
|
|
308
|
+
}
|
|
309
|
+
}
|
|
305
310
|
/**
|
|
306
311
|
* Check if a key event matches a key pattern
|
|
307
312
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lanonasis/cli",
|
|
3
|
-
"version": "3.9.
|
|
3
|
+
"version": "3.9.6",
|
|
4
4
|
"description": "Professional CLI for LanOnasis Memory as a Service (MaaS) with MCP support, seamless inline editing, and enterprise-grade security",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lanonasis",
|
|
@@ -73,14 +73,14 @@
|
|
|
73
73
|
"zod": "^3.24.4"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
|
-
"@jest/globals": "^
|
|
76
|
+
"@jest/globals": "^27.5.1",
|
|
77
77
|
"@types/cli-progress": "^3.11.6",
|
|
78
78
|
"@types/inquirer": "^9.0.7",
|
|
79
79
|
"@types/node": "^22.19.3",
|
|
80
80
|
"@types/ws": "^8.5.12",
|
|
81
81
|
"fast-check": "^3.15.1",
|
|
82
|
-
"jest": "^
|
|
83
|
-
"rimraf": "^
|
|
82
|
+
"jest": "^25.0.0",
|
|
83
|
+
"rimraf": "^6.1.3",
|
|
84
84
|
"ts-jest": "^29.1.1",
|
|
85
85
|
"typescript": "^5.7.2"
|
|
86
86
|
},
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"build": "rimraf dist && tsc -p tsconfig.json",
|
|
89
89
|
"prepublishOnly": "npm run build",
|
|
90
90
|
"postinstall": "node scripts/postinstall.js",
|
|
91
|
-
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
91
|
+
"test": "[ -f node_modules/jest/bin/jest.js ] || bun install --no-save; node --experimental-vm-modules node_modules/jest/bin/jest.js",
|
|
92
92
|
"test:watch": "npm test -- --watch",
|
|
93
93
|
"test:coverage": "npm test -- --coverage"
|
|
94
94
|
}
|