gitarsenal-cli 1.9.112 → 1.9.113

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/.venv_status.json CHANGED
@@ -1 +1 @@
1
- {"created":"2025-10-15T14:03:01.884Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
1
+ {"created":"2025-10-15T14:18:56.158Z","packages":["modal","gitingest","requests","anthropic"],"uv_version":"uv 0.8.4 (Homebrew 2025-07-30)"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gitarsenal-cli",
3
- "version": "1.9.112",
3
+ "version": "1.9.113",
4
4
  "description": "CLI tool for creating Modal sandboxes with GitHub repositories",
5
5
  "main": "index.js",
6
6
  "bin": {
package/tui-app/index.jsx CHANGED
@@ -380,7 +380,7 @@ const AuthChoice = ({ selectedIndex }) => {
380
380
  );
381
381
  };
382
382
 
383
- const LoginForm = ({ values, onInput, onSubmit }) => {
383
+ const LoginForm = ({ values, onInput, onSubmit, onNextField, focusedFieldIndex }) => {
384
384
  const fields = ['username', 'email', 'fullName', 'password'];
385
385
  const labels = ['Username:', 'Email Address:', 'Full Name:', 'Password:'];
386
386
 
@@ -395,21 +395,21 @@ const LoginForm = ({ values, onInput, onSubmit }) => {
395
395
  <input
396
396
  value={values[field] || ''}
397
397
  onInput={(value) => onInput(field, value)}
398
- onSubmit={index === fields.length - 1 ? onSubmit : undefined}
398
+ onSubmit={index === fields.length - 1 ? onSubmit : () => onNextField(index)}
399
399
  placeholder={field === 'password' ? '••••••••' : ''}
400
- focused={index === 0}
400
+ focused={index === focusedFieldIndex}
401
401
  width={50}
402
402
  />
403
403
  </box>
404
404
  ))}
405
405
  <box marginTop={1}>
406
- <text dimColor fg="gray">Fill all fields and press Enter • Esc to go back</text>
406
+ <text dimColor fg="gray">Tab or Enter to next field • Esc to go back</text>
407
407
  </box>
408
408
  </box>
409
409
  );
410
410
  };
411
411
 
412
- const RegisterForm = ({ values, onInput, onSubmit }) => {
412
+ const RegisterForm = ({ values, onInput, onSubmit, onNextField, focusedFieldIndex }) => {
413
413
  const fields = ['username', 'email', 'fullName', 'password', 'confirmPassword'];
414
414
  const labels = ['Username:', 'Email Address:', 'Full Name:', 'Password (min 8 chars):', 'Confirm Password:'];
415
415
 
@@ -424,15 +424,60 @@ const RegisterForm = ({ values, onInput, onSubmit }) => {
424
424
  <input
425
425
  value={values[field] || ''}
426
426
  onInput={(value) => onInput(field, value)}
427
- onSubmit={index === fields.length - 1 ? onSubmit : undefined}
427
+ onSubmit={index === fields.length - 1 ? onSubmit : () => onNextField(index)}
428
428
  placeholder={field.includes('password') ? '••••••••' : ''}
429
- focused={index === 0}
429
+ focused={index === focusedFieldIndex}
430
430
  width={50}
431
431
  />
432
432
  </box>
433
433
  ))}
434
434
  <box marginTop={1}>
435
- <text dimColor fg="gray">Fill all fields and press Enter • Esc to go back</text>
435
+ <text dimColor fg="gray">Tab or Enter to next field • Esc to go back</text>
436
+ </box>
437
+ </box>
438
+ );
439
+ };
440
+
441
+ const Settings = ({ userCredentials, selectedIndex }) => {
442
+ const settingsOptions = userCredentials
443
+ ? ['Create New Account', 'Logout', 'Back to Menu']
444
+ : ['Create Account', 'Login', 'Back to Menu'];
445
+
446
+ return (
447
+ <box style={{ flexDirection: 'column', alignItems: 'center' }}>
448
+ <box borderStyle="single" padding={1} marginBottom={1}>
449
+ <text bold>Settings</text>
450
+ </box>
451
+
452
+ {userCredentials ? (
453
+ <box marginBottom={2} style={{ flexDirection: 'column', alignItems: 'center' }}>
454
+ <text fg="green">Logged in as: {userCredentials.userName}</text>
455
+ <text dimColor fg="gray">{userCredentials.userEmail}</text>
456
+ </box>
457
+ ) : (
458
+ <box marginBottom={2} style={{ flexDirection: 'column', alignItems: 'center' }}>
459
+ <text dimColor fg="gray">No account logged in</text>
460
+ </box>
461
+ )}
462
+
463
+ {settingsOptions.map((option, index) => (
464
+ <box key={index} marginY={0} marginBottom={0}>
465
+ {index === selectedIndex ? (
466
+ <box borderStyle="single" borderColor="cyan" paddingX={2} paddingY={0} width={48} style={{ justifyContent: 'center' }}>
467
+ <text bold fg="cyan">{option}</text>
468
+ </box>
469
+ ) : (
470
+ <box borderStyle="single" borderColor="gray" paddingX={2} paddingY={0} width={48} style={{ justifyContent: 'center' }}>
471
+ <text dimColor>{option}</text>
472
+ </box>
473
+ )}
474
+ </box>
475
+ ))}
476
+
477
+ <box marginTop={2} style={{ flexDirection: 'column', alignItems: 'center' }}>
478
+ <text bold>Controls:</text>
479
+ <text dimColor fg="gray">↑↓ - Navigate • Enter - Select</text>
480
+ <text dimColor fg="gray">Esc - Back to menu</text>
436
481
  </box>
437
482
  </box>
438
483
  );
@@ -498,7 +543,7 @@ const ApiKeysManagement = ({ apiKeys, selectedIndex }) => {
498
543
  );
499
544
  };
500
545
 
501
- const ApiKeyForm = ({ serviceName, apiKey, onServiceInput, onKeyInput, onSubmit, isEditing }) => {
546
+ const ApiKeyForm = ({ serviceName, apiKey, onServiceInput, onKeyInput, onSubmit, onNextField, isEditing, focusedFieldIndex }) => {
502
547
  return (
503
548
  <box style={{ flexDirection: 'column', alignItems: 'center' }}>
504
549
  <box borderStyle="single" padding={1} marginBottom={1}>
@@ -524,8 +569,9 @@ const ApiKeyForm = ({ serviceName, apiKey, onServiceInput, onKeyInput, onSubmit,
524
569
  <input
525
570
  value={serviceName || ''}
526
571
  onInput={onServiceInput}
572
+ onSubmit={onNextField}
527
573
  placeholder="e.g., openai_api_key"
528
- focused={true}
574
+ focused={focusedFieldIndex === 0}
529
575
  width={70}
530
576
  />
531
577
  </box>
@@ -545,13 +591,13 @@ const ApiKeyForm = ({ serviceName, apiKey, onServiceInput, onKeyInput, onSubmit,
545
591
  onInput={onKeyInput}
546
592
  onSubmit={onSubmit}
547
593
  placeholder="Paste your API key here..."
548
- focused={isEditing}
594
+ focused={isEditing || focusedFieldIndex === 1}
549
595
  width={70}
550
596
  />
551
597
  </box>
552
598
 
553
599
  <box marginTop={1} style={{ flexDirection: 'column', alignItems: 'center' }}>
554
- <text dimColor fg="gray">Enter to save • Esc to cancel</text>
600
+ <text dimColor fg="gray">{isEditing ? 'Enter to save' : 'Tab or Enter to next field'} • Esc to cancel</text>
555
601
  {isEditing && <text dimColor fg="gray">Clear field to remove the key</text>}
556
602
  </box>
557
603
  </box>
@@ -682,11 +728,13 @@ const App = () => {
682
728
  const [viewingSandboxId, setViewingSandboxId] = useState(null);
683
729
  const [userCredentials, setUserCredentials] = useState(null);
684
730
  const [authFormValues, setAuthFormValues] = useState({});
731
+ const [authFieldIndex, setAuthFieldIndex] = useState(0);
685
732
  const [apiKeys, setApiKeys] = useState({});
686
733
  const [editingServiceName, setEditingServiceName] = useState('');
687
734
  const [serviceNameInput, setServiceNameInput] = useState('');
688
735
  const [apiKeyInput, setApiKeyInput] = useState('');
689
736
  const [isEditingExisting, setIsEditingExisting] = useState(false);
737
+ const [apiKeyFieldIndex, setApiKeyFieldIndex] = useState(0);
690
738
 
691
739
  // Load user credentials on mount
692
740
  useEffect(() => {
@@ -819,8 +867,14 @@ const App = () => {
819
867
  setServiceNameInput('');
820
868
  setApiKeyInput('');
821
869
  setIsEditingExisting(false);
870
+ setApiKeyFieldIndex(0);
822
871
  setScreen('apiKeysManagement');
823
872
  setSelectedIndex(0);
873
+ } else if (key.name === 'tab') {
874
+ // Toggle between fields when adding new key
875
+ if (!isEditingExisting) {
876
+ setApiKeyFieldIndex((prev) => (prev === 0 ? 1 : 0));
877
+ }
824
878
  }
825
879
  // Let the input component handle all other keys
826
880
  return;
@@ -841,6 +895,10 @@ const App = () => {
841
895
  } else if (selectedIndex === 2) {
842
896
  setScreen('apiKeysManagement');
843
897
  setSelectedIndex(0);
898
+ } else if (selectedIndex === 3) {
899
+ // Settings
900
+ setScreen('settings');
901
+ setSelectedIndex(0);
844
902
  } else if (selectedIndex === 4) {
845
903
  // Help & Examples - open docs page
846
904
  openUrl('https://gitarsenal.dev/docs');
@@ -917,25 +975,35 @@ const App = () => {
917
975
  if (selectedIndex === 0) {
918
976
  setScreen('register');
919
977
  setAuthFormValues({});
978
+ setAuthFieldIndex(0);
920
979
  } else {
921
980
  setScreen('login');
922
981
  setAuthFormValues({});
982
+ setAuthFieldIndex(0);
923
983
  }
924
984
  } else if (key.name === 'escape') {
925
985
  process.exit(0);
926
986
  }
927
987
  } else if (screen === 'login') {
988
+ const loginFields = 4;
928
989
  if (key.name === 'escape') {
929
- setScreen('authChoice');
990
+ setScreen('settings');
930
991
  setSelectedIndex(0);
931
992
  setAuthFormValues({});
993
+ setAuthFieldIndex(0);
994
+ } else if (key.name === 'tab') {
995
+ setAuthFieldIndex((prev) => (prev < loginFields - 1 ? prev + 1 : 0));
932
996
  }
933
997
  // Submit is handled by input component's onSubmit
934
998
  } else if (screen === 'register') {
999
+ const registerFields = 5;
935
1000
  if (key.name === 'escape') {
936
- setScreen('authChoice');
1001
+ setScreen('settings');
937
1002
  setSelectedIndex(0);
938
1003
  setAuthFormValues({});
1004
+ setAuthFieldIndex(0);
1005
+ } else if (key.name === 'tab') {
1006
+ setAuthFieldIndex((prev) => (prev < registerFields - 1 ? prev + 1 : 0));
939
1007
  }
940
1008
  // Submit is handled by input component's onSubmit
941
1009
  } else if (screen === 'sandboxList') {
@@ -967,6 +1035,59 @@ const App = () => {
967
1035
  setViewingSandboxId(null);
968
1036
  setScreen('sandboxList');
969
1037
  }
1038
+ } else if (screen === 'settings') {
1039
+ const settingsOptions = userCredentials ? 3 : 3; // Always 3 options
1040
+
1041
+ if (key.name === 'up') {
1042
+ setSelectedIndex((prev) => (prev > 0 ? prev - 1 : settingsOptions - 1));
1043
+ } else if (key.name === 'down') {
1044
+ setSelectedIndex((prev) => (prev < settingsOptions - 1 ? prev + 1 : 0));
1045
+ } else if (key.name === 'return' || key.name === 'enter') {
1046
+ if (userCredentials) {
1047
+ // Logged in: Create New Account, Logout, Back to Menu
1048
+ if (selectedIndex === 0) {
1049
+ // Create New Account
1050
+ setScreen('register');
1051
+ setAuthFormValues({});
1052
+ setAuthFieldIndex(0);
1053
+ } else if (selectedIndex === 1) {
1054
+ // Logout
1055
+ setUserCredentials(null);
1056
+ const { userConfigPath } = getUserConfigPath();
1057
+ if (fs.existsSync(userConfigPath)) {
1058
+ fs.unlinkSync(userConfigPath);
1059
+ }
1060
+ setStatusMessage('Logged out successfully');
1061
+ setTimeout(() => setStatusMessage(''), 3000);
1062
+ setScreen('menu');
1063
+ setSelectedIndex(0);
1064
+ } else if (selectedIndex === 2) {
1065
+ // Back to Menu
1066
+ setScreen('menu');
1067
+ setSelectedIndex(0);
1068
+ }
1069
+ } else {
1070
+ // Not logged in: Create Account, Login, Back to Menu
1071
+ if (selectedIndex === 0) {
1072
+ // Create Account
1073
+ setScreen('register');
1074
+ setAuthFormValues({});
1075
+ setAuthFieldIndex(0);
1076
+ } else if (selectedIndex === 1) {
1077
+ // Login
1078
+ setScreen('login');
1079
+ setAuthFormValues({});
1080
+ setAuthFieldIndex(0);
1081
+ } else if (selectedIndex === 2) {
1082
+ // Back to Menu
1083
+ setScreen('menu');
1084
+ setSelectedIndex(0);
1085
+ }
1086
+ }
1087
+ } else if (key.name === 'escape') {
1088
+ setScreen('menu');
1089
+ setSelectedIndex(0);
1090
+ }
970
1091
  } else if (screen === 'apiKeysManagement') {
971
1092
  // Calculate items: 1 "Add New" + all stored keys
972
1093
  const storedKeyNames = Object.keys(apiKeys).sort();
@@ -982,6 +1103,7 @@ const App = () => {
982
1103
  setServiceNameInput('');
983
1104
  setApiKeyInput('');
984
1105
  setIsEditingExisting(false);
1106
+ setApiKeyFieldIndex(0);
985
1107
  setScreen('apiKeyForm');
986
1108
  } else {
987
1109
  // One of the stored keys selected - edit it
@@ -990,6 +1112,7 @@ const App = () => {
990
1112
  setServiceNameInput(serviceName);
991
1113
  setApiKeyInput(apiKeys[serviceName] || '');
992
1114
  setIsEditingExisting(true);
1115
+ setApiKeyFieldIndex(0);
993
1116
  setScreen('apiKeyForm');
994
1117
  }
995
1118
  } else if (key.name === 'd') {
@@ -1030,6 +1153,13 @@ const App = () => {
1030
1153
  setAuthFormValues(prev => ({ ...prev, [field]: value }));
1031
1154
  };
1032
1155
 
1156
+ const handleAuthNextField = (currentIndex) => {
1157
+ const maxFields = screen === 'register' ? 4 : 3; // register has 5 fields (0-4), login has 4 fields (0-3)
1158
+ if (currentIndex < maxFields) {
1159
+ setAuthFieldIndex(currentIndex + 1);
1160
+ }
1161
+ };
1162
+
1033
1163
  const handleLoginSubmit = () => {
1034
1164
  const { username, email, fullName, password } = authFormValues;
1035
1165
  if (username && email && fullName && password) {
@@ -1037,9 +1167,11 @@ const App = () => {
1037
1167
  const saved = saveUserCredentials(username, fullName, email);
1038
1168
  if (saved) {
1039
1169
  setUserCredentials({ userId: username, userName: fullName, userEmail: email });
1040
- setStatusMessage(`Welcome, ${fullName}!`);
1170
+ setStatusMessage(`Welcome back, ${fullName}!`);
1041
1171
  setTimeout(() => setStatusMessage(''), 3000);
1042
- createSandbox();
1172
+ setScreen('settings');
1173
+ setSelectedIndex(0);
1174
+ setAuthFormValues({});
1043
1175
  } else {
1044
1176
  setStatusMessage('Failed to save credentials');
1045
1177
  setTimeout(() => setStatusMessage(''), 3000);
@@ -1083,7 +1215,9 @@ const App = () => {
1083
1215
  setUserCredentials({ userId: username, userName: fullName, userEmail: email });
1084
1216
  setStatusMessage(`Account created! Welcome, ${fullName}!`);
1085
1217
  setTimeout(() => setStatusMessage(''), 3000);
1086
- createSandbox();
1218
+ setScreen('settings');
1219
+ setSelectedIndex(0);
1220
+ setAuthFormValues({});
1087
1221
  } else {
1088
1222
  setStatusMessage('Failed to save credentials');
1089
1223
  setTimeout(() => setStatusMessage(''), 3000);
@@ -1098,6 +1232,13 @@ const App = () => {
1098
1232
  setApiKeyInput(value);
1099
1233
  };
1100
1234
 
1235
+ const handleApiKeyNextField = () => {
1236
+ // Move from Service Name (0) to API Key (1)
1237
+ if (apiKeyFieldIndex === 0) {
1238
+ setApiKeyFieldIndex(1);
1239
+ }
1240
+ };
1241
+
1101
1242
  const handleApiKeyFormSubmit = () => {
1102
1243
  const serviceName = isEditingExisting ? editingServiceName : serviceNameInput.trim();
1103
1244
  const apiKey = apiKeyInput.trim();
@@ -1136,6 +1277,7 @@ const App = () => {
1136
1277
  setServiceNameInput('');
1137
1278
  setApiKeyInput('');
1138
1279
  setIsEditingExisting(false);
1280
+ setApiKeyFieldIndex(0);
1139
1281
  setScreen('apiKeysManagement');
1140
1282
  setSelectedIndex(0);
1141
1283
  } else {
@@ -1161,12 +1303,13 @@ const App = () => {
1161
1303
  {screen === 'gpuCountSelection' && <GpuCountSelection selectedIndex={selectedIndex} gpuType={config.gpuType} />}
1162
1304
  {screen === 'confirmation' && <Confirmation config={config} />}
1163
1305
  {screen === 'authChoice' && <AuthChoice selectedIndex={selectedIndex} />}
1164
- {screen === 'login' && <LoginForm values={authFormValues} onInput={handleAuthFormInput} onSubmit={handleLoginSubmit} />}
1165
- {screen === 'register' && <RegisterForm values={authFormValues} onInput={handleAuthFormInput} onSubmit={handleRegisterSubmit} />}
1306
+ {screen === 'login' && <LoginForm values={authFormValues} onInput={handleAuthFormInput} onSubmit={handleLoginSubmit} onNextField={handleAuthNextField} focusedFieldIndex={authFieldIndex} />}
1307
+ {screen === 'register' && <RegisterForm values={authFormValues} onInput={handleAuthFormInput} onSubmit={handleRegisterSubmit} onNextField={handleAuthNextField} focusedFieldIndex={authFieldIndex} />}
1166
1308
  {screen === 'sandboxList' && <SandboxList sandboxes={sandboxes} selectedIndex={selectedIndex} />}
1167
1309
  {screen === 'sandboxLogs' && viewingSandbox && <SandboxLogs sandbox={viewingSandbox} />}
1310
+ {screen === 'settings' && <Settings userCredentials={userCredentials} selectedIndex={selectedIndex} />}
1168
1311
  {screen === 'apiKeysManagement' && <ApiKeysManagement apiKeys={apiKeys} selectedIndex={selectedIndex} />}
1169
- {screen === 'apiKeyForm' && <ApiKeyForm serviceName={serviceNameInput} apiKey={apiKeyInput} onServiceInput={handleServiceNameInput} onKeyInput={handleApiKeyInput} onSubmit={handleApiKeyFormSubmit} isEditing={isEditingExisting} />}
1312
+ {screen === 'apiKeyForm' && <ApiKeyForm serviceName={serviceNameInput} apiKey={apiKeyInput} onServiceInput={handleServiceNameInput} onKeyInput={handleApiKeyInput} onSubmit={handleApiKeyFormSubmit} onNextField={handleApiKeyNextField} isEditing={isEditingExisting} focusedFieldIndex={apiKeyFieldIndex} />}
1170
1313
  </box>
1171
1314
  );
1172
1315
  };