indusagi-coding-agent 0.1.14 → 0.1.21
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 +80 -0
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +19 -0
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/login-handler.d.ts +20 -0
- package/dist/cli/login-handler.d.ts.map +1 -0
- package/dist/cli/login-handler.js +413 -0
- package/dist/cli/login-handler.js.map +1 -0
- package/dist/core/auth-storage.d.ts +56 -19
- package/dist/core/auth-storage.d.ts.map +1 -1
- package/dist/core/auth-storage.js +207 -53
- package/dist/core/auth-storage.js.map +1 -1
- package/dist/core/model-registry.d.ts +17 -2
- package/dist/core/model-registry.d.ts.map +1 -1
- package/dist/core/model-registry.js +20 -4
- package/dist/core/model-registry.js.map +1 -1
- package/dist/core/model-resolver.js +2 -2
- package/dist/core/model-resolver.js.map +1 -1
- package/dist/core/sdk.d.ts.map +1 -1
- package/dist/core/sdk.js +10 -1
- package/dist/core/sdk.js.map +1 -1
- package/dist/core/task-session-manager.d.ts.map +1 -1
- package/dist/core/task-session-manager.js +7 -1
- package/dist/core/task-session-manager.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +9 -1
- package/dist/main.js.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.d.ts +28 -0
- package/dist/modes/interactive/components/oauth-selector.d.ts.map +1 -1
- package/dist/modes/interactive/components/oauth-selector.js +161 -27
- package/dist/modes/interactive/components/oauth-selector.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +20 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +265 -1
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/package.json +3 -3
|
@@ -37,6 +37,7 @@ import { ExtensionSelectorComponent } from "./components/extension-selector.js";
|
|
|
37
37
|
import { FooterComponent } from "./components/footer.js";
|
|
38
38
|
import { appKey, editorKey } from "./components/keybinding-hints.js";
|
|
39
39
|
import { LoginDialogComponent } from "./components/login-dialog.js";
|
|
40
|
+
import { AccountSelectorComponent } from "./components/oauth-selector.js";
|
|
40
41
|
import { ModelSelectorComponent } from "./components/model-selector.js";
|
|
41
42
|
import { OAuthSelectorComponent } from "./components/oauth-selector.js";
|
|
42
43
|
import { ScopedModelsSelectorComponent } from "./components/scoped-models-selector.js";
|
|
@@ -3010,6 +3011,14 @@ export class InteractiveMode {
|
|
|
3010
3011
|
const selector = new OAuthSelectorComponent(mode, this.session.modelRegistry.authStorage, async (selection) => {
|
|
3011
3012
|
done();
|
|
3012
3013
|
if (mode === "login") {
|
|
3014
|
+
// Check if provider has existing accounts
|
|
3015
|
+
const accounts = this.session.modelRegistry.authStorage.getAccounts(selection.id);
|
|
3016
|
+
if (accounts.length >= 1) {
|
|
3017
|
+
// Show account selector for providers with existing accounts
|
|
3018
|
+
// This allows switching between accounts or adding new ones
|
|
3019
|
+
await this.showAccountSelector(selection.id, selection.name, accounts);
|
|
3020
|
+
return;
|
|
3021
|
+
}
|
|
3013
3022
|
if (selection.kind === "oauth") {
|
|
3014
3023
|
await this.showLoginDialog(selection.id);
|
|
3015
3024
|
return;
|
|
@@ -3048,6 +3057,198 @@ export class InteractiveMode {
|
|
|
3048
3057
|
return { component: selector, focus: selector };
|
|
3049
3058
|
});
|
|
3050
3059
|
}
|
|
3060
|
+
/**
|
|
3061
|
+
* Show account selector for providers with multiple accounts
|
|
3062
|
+
*/
|
|
3063
|
+
async showAccountSelector(providerId, providerName, accounts) {
|
|
3064
|
+
this.showSelector((done) => {
|
|
3065
|
+
const selector = new AccountSelectorComponent(providerName, accounts, (accountId) => {
|
|
3066
|
+
// Select existing account
|
|
3067
|
+
done();
|
|
3068
|
+
this.session.modelRegistry.authStorage.setDefaultAccount(providerId, accountId);
|
|
3069
|
+
this.showStatus(`Switched to account: ${accountId}`);
|
|
3070
|
+
}, () => {
|
|
3071
|
+
// Cancel
|
|
3072
|
+
done();
|
|
3073
|
+
this.ui.requestRender();
|
|
3074
|
+
}, async () => {
|
|
3075
|
+
// Add new account - prompt for account name first
|
|
3076
|
+
done();
|
|
3077
|
+
await this.showNewAccountNameDialog(providerId, providerName);
|
|
3078
|
+
});
|
|
3079
|
+
return { component: selector, focus: selector };
|
|
3080
|
+
});
|
|
3081
|
+
}
|
|
3082
|
+
/**
|
|
3083
|
+
* Prompt for account name, then proceed with OAuth or API key login
|
|
3084
|
+
*/
|
|
3085
|
+
async showNewAccountNameDialog(providerId, providerName) {
|
|
3086
|
+
const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => {
|
|
3087
|
+
// Completion handled below
|
|
3088
|
+
}, `Add new account for ${providerName}`);
|
|
3089
|
+
// Show dialog in editor container
|
|
3090
|
+
this.editorContainer.clear();
|
|
3091
|
+
this.editorContainer.addChild(dialog);
|
|
3092
|
+
this.ui.setFocus(dialog);
|
|
3093
|
+
this.ui.requestRender();
|
|
3094
|
+
const restoreEditor = () => {
|
|
3095
|
+
this.editorContainer.clear();
|
|
3096
|
+
this.editorContainer.addChild(this.editor);
|
|
3097
|
+
this.ui.setFocus(this.editor);
|
|
3098
|
+
this.ui.requestRender();
|
|
3099
|
+
};
|
|
3100
|
+
try {
|
|
3101
|
+
// Prompt for account name
|
|
3102
|
+
const accountName = await dialog.showPrompt(`Enter a name for this account (e.g., 'work', 'personal'):`, "my-account");
|
|
3103
|
+
const trimmedName = accountName.trim();
|
|
3104
|
+
if (!trimmedName) {
|
|
3105
|
+
restoreEditor();
|
|
3106
|
+
this.showWarning("Account name cannot be empty.");
|
|
3107
|
+
return;
|
|
3108
|
+
}
|
|
3109
|
+
// Check if account name already exists
|
|
3110
|
+
const existingAccounts = this.session.modelRegistry.authStorage.getAccounts(providerId);
|
|
3111
|
+
if (existingAccounts.some(a => a.accountId === trimmedName || a.credential.accountName === trimmedName)) {
|
|
3112
|
+
restoreEditor();
|
|
3113
|
+
this.showWarning(`Account '${trimmedName}' already exists for ${providerName}.`);
|
|
3114
|
+
return;
|
|
3115
|
+
}
|
|
3116
|
+
restoreEditor();
|
|
3117
|
+
// Determine if OAuth or API key provider
|
|
3118
|
+
const oauthProviders = getOAuthProviders();
|
|
3119
|
+
const isOAuth = oauthProviders.some((p) => p.id === providerId);
|
|
3120
|
+
if (isOAuth) {
|
|
3121
|
+
// Proceed with OAuth login using the account name
|
|
3122
|
+
await this.showLoginDialogWithAccountName(providerId, trimmedName);
|
|
3123
|
+
}
|
|
3124
|
+
else {
|
|
3125
|
+
// For API key providers, prompt for the key
|
|
3126
|
+
await this.showApiKeyInputDialogWithName(providerId, providerName, trimmedName);
|
|
3127
|
+
}
|
|
3128
|
+
}
|
|
3129
|
+
catch (error) {
|
|
3130
|
+
restoreEditor();
|
|
3131
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3132
|
+
if (errorMsg !== "Login cancelled") {
|
|
3133
|
+
this.showError(`Failed to add account: ${errorMsg}`);
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
}
|
|
3137
|
+
/**
|
|
3138
|
+
* OAuth login with a specific account name
|
|
3139
|
+
*/
|
|
3140
|
+
async showLoginDialogWithAccountName(providerId, accountName) {
|
|
3141
|
+
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
|
3142
|
+
const providerName = providerInfo?.name || providerId;
|
|
3143
|
+
const usesCallbackServer = providerInfo?.usesCallbackServer ?? false;
|
|
3144
|
+
const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => {
|
|
3145
|
+
// Completion handled below
|
|
3146
|
+
}, `Login to ${providerName} (${accountName})`);
|
|
3147
|
+
this.editorContainer.clear();
|
|
3148
|
+
this.editorContainer.addChild(dialog);
|
|
3149
|
+
this.ui.setFocus(dialog);
|
|
3150
|
+
this.ui.requestRender();
|
|
3151
|
+
let manualCodeResolve;
|
|
3152
|
+
let manualCodeReject;
|
|
3153
|
+
const manualCodePromise = new Promise((resolve, reject) => {
|
|
3154
|
+
manualCodeResolve = resolve;
|
|
3155
|
+
manualCodeReject = reject;
|
|
3156
|
+
});
|
|
3157
|
+
const restoreEditor = () => {
|
|
3158
|
+
this.editorContainer.clear();
|
|
3159
|
+
this.editorContainer.addChild(this.editor);
|
|
3160
|
+
this.ui.setFocus(this.editor);
|
|
3161
|
+
this.ui.requestRender();
|
|
3162
|
+
};
|
|
3163
|
+
try {
|
|
3164
|
+
await this.session.modelRegistry.authStorage.login(providerId, {
|
|
3165
|
+
onAuth: (info) => {
|
|
3166
|
+
dialog.showAuth(info.url, info.instructions);
|
|
3167
|
+
if (usesCallbackServer) {
|
|
3168
|
+
dialog
|
|
3169
|
+
.showManualInput("Paste redirect URL below, or complete login in browser:")
|
|
3170
|
+
.then((value) => {
|
|
3171
|
+
if (value && manualCodeResolve) {
|
|
3172
|
+
manualCodeResolve(value);
|
|
3173
|
+
manualCodeResolve = undefined;
|
|
3174
|
+
}
|
|
3175
|
+
})
|
|
3176
|
+
.catch(() => {
|
|
3177
|
+
if (manualCodeReject) {
|
|
3178
|
+
manualCodeReject(new Error("Login cancelled"));
|
|
3179
|
+
manualCodeReject = undefined;
|
|
3180
|
+
}
|
|
3181
|
+
});
|
|
3182
|
+
}
|
|
3183
|
+
else if (providerId === "github-copilot") {
|
|
3184
|
+
dialog.showWaiting("Waiting for browser authentication...");
|
|
3185
|
+
}
|
|
3186
|
+
},
|
|
3187
|
+
onPrompt: async (prompt) => {
|
|
3188
|
+
return dialog.showPrompt(prompt.message, prompt.placeholder);
|
|
3189
|
+
},
|
|
3190
|
+
onProgress: (message) => {
|
|
3191
|
+
dialog.showProgress(message);
|
|
3192
|
+
},
|
|
3193
|
+
onManualCodeInput: () => manualCodePromise,
|
|
3194
|
+
signal: dialog.signal,
|
|
3195
|
+
}, accountName, // Pass the account name
|
|
3196
|
+
accountName);
|
|
3197
|
+
restoreEditor();
|
|
3198
|
+
this.session.modelRegistry.refresh();
|
|
3199
|
+
await this.updateAvailableProviderCount();
|
|
3200
|
+
this.showStatus(`Logged in to ${providerName} as '${accountName}'. Credentials saved to ${getAuthPath()}`);
|
|
3201
|
+
}
|
|
3202
|
+
catch (error) {
|
|
3203
|
+
restoreEditor();
|
|
3204
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3205
|
+
if (errorMsg !== "Login cancelled") {
|
|
3206
|
+
this.showError(`Failed to login to ${providerName}: ${errorMsg}`);
|
|
3207
|
+
}
|
|
3208
|
+
}
|
|
3209
|
+
}
|
|
3210
|
+
/**
|
|
3211
|
+
* API key input with a specific account name
|
|
3212
|
+
*/
|
|
3213
|
+
async showApiKeyInputDialogWithName(providerId, providerName, accountName) {
|
|
3214
|
+
const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => { }, `Add API key for ${providerName} (${accountName})`);
|
|
3215
|
+
this.editorContainer.clear();
|
|
3216
|
+
this.editorContainer.addChild(dialog);
|
|
3217
|
+
this.ui.setFocus(dialog);
|
|
3218
|
+
this.ui.requestRender();
|
|
3219
|
+
const restoreEditor = () => {
|
|
3220
|
+
this.editorContainer.clear();
|
|
3221
|
+
this.editorContainer.addChild(this.editor);
|
|
3222
|
+
this.ui.setFocus(this.editor);
|
|
3223
|
+
this.ui.requestRender();
|
|
3224
|
+
};
|
|
3225
|
+
try {
|
|
3226
|
+
const apiKey = await dialog.showPrompt(`Enter API key for ${providerName} (${accountName}):`, "sk-...");
|
|
3227
|
+
const trimmedKey = apiKey.trim();
|
|
3228
|
+
if (!trimmedKey) {
|
|
3229
|
+
restoreEditor();
|
|
3230
|
+
this.showWarning("API key cannot be empty.");
|
|
3231
|
+
return;
|
|
3232
|
+
}
|
|
3233
|
+
this.session.modelRegistry.authStorage.set(providerId, accountName, {
|
|
3234
|
+
type: "api_key",
|
|
3235
|
+
key: trimmedKey,
|
|
3236
|
+
accountId: accountName,
|
|
3237
|
+
accountName: accountName,
|
|
3238
|
+
});
|
|
3239
|
+
this.session.modelRegistry.refresh();
|
|
3240
|
+
await this.updateAvailableProviderCount();
|
|
3241
|
+
restoreEditor();
|
|
3242
|
+
this.showStatus(`Saved API key for ${providerName}/${accountName}. Credentials saved to ${getAuthPath()}`);
|
|
3243
|
+
}
|
|
3244
|
+
catch (error) {
|
|
3245
|
+
restoreEditor();
|
|
3246
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3247
|
+
if (errorMsg !== "Login cancelled") {
|
|
3248
|
+
this.showError(`Failed to save API key: ${errorMsg}`);
|
|
3249
|
+
}
|
|
3250
|
+
}
|
|
3251
|
+
}
|
|
3051
3252
|
async showLoginDialog(providerId) {
|
|
3052
3253
|
const providerInfo = getOAuthProviders().find((p) => p.id === providerId);
|
|
3053
3254
|
const providerName = providerInfo?.name || providerId;
|
|
@@ -3150,7 +3351,7 @@ export class InteractiveMode {
|
|
|
3150
3351
|
this.showWarning("API key cannot be empty.");
|
|
3151
3352
|
return;
|
|
3152
3353
|
}
|
|
3153
|
-
this.session.modelRegistry.authStorage.set(selection.id, { type: "api_key", key: trimmedKey });
|
|
3354
|
+
this.session.modelRegistry.authStorage.set(selection.id, "default", { type: "api_key", key: trimmedKey });
|
|
3154
3355
|
this.session.modelRegistry.refresh();
|
|
3155
3356
|
await this.updateAvailableProviderCount();
|
|
3156
3357
|
restoreEditor();
|
|
@@ -3165,6 +3366,69 @@ export class InteractiveMode {
|
|
|
3165
3366
|
}
|
|
3166
3367
|
}
|
|
3167
3368
|
}
|
|
3369
|
+
/**
|
|
3370
|
+
* Show API key input dialog for adding a new account
|
|
3371
|
+
*/
|
|
3372
|
+
async showApiKeyInputDialog(providerId, providerName) {
|
|
3373
|
+
const dialog = new LoginDialogComponent(this.ui, providerId, (_success, _message) => {
|
|
3374
|
+
// Completion handled below
|
|
3375
|
+
}, `Add new account for ${providerName}`);
|
|
3376
|
+
// Show dialog in editor container
|
|
3377
|
+
this.editorContainer.clear();
|
|
3378
|
+
this.editorContainer.addChild(dialog);
|
|
3379
|
+
this.ui.setFocus(dialog);
|
|
3380
|
+
this.ui.requestRender();
|
|
3381
|
+
const restoreEditor = () => {
|
|
3382
|
+
this.editorContainer.clear();
|
|
3383
|
+
this.editorContainer.addChild(this.editor);
|
|
3384
|
+
this.ui.setFocus(this.editor);
|
|
3385
|
+
this.ui.requestRender();
|
|
3386
|
+
};
|
|
3387
|
+
try {
|
|
3388
|
+
// Prompt for account name
|
|
3389
|
+
const accountName = await dialog.showPrompt(`Enter a name for this account (e.g., 'work', 'personal'):`, "my-account");
|
|
3390
|
+
const trimmedName = accountName.trim();
|
|
3391
|
+
if (!trimmedName) {
|
|
3392
|
+
restoreEditor();
|
|
3393
|
+
this.showWarning("Account name cannot be empty.");
|
|
3394
|
+
return;
|
|
3395
|
+
}
|
|
3396
|
+
// Check if account name already exists
|
|
3397
|
+
const existingAccounts = this.session.modelRegistry.authStorage.getAccounts(providerId);
|
|
3398
|
+
if (existingAccounts.some(a => (a.credential.accountName || a.accountId) === trimmedName)) {
|
|
3399
|
+
restoreEditor();
|
|
3400
|
+
this.showWarning(`Account '${trimmedName}' already exists for ${providerName}.`);
|
|
3401
|
+
return;
|
|
3402
|
+
}
|
|
3403
|
+
// Prompt for API key
|
|
3404
|
+
dialog.showProgress(`Enter API key for account '${trimmedName}':`);
|
|
3405
|
+
const apiKey = await dialog.showPrompt(`Enter API key for ${providerName} (${trimmedName}):`, "sk-...");
|
|
3406
|
+
const trimmedKey = apiKey.trim();
|
|
3407
|
+
if (!trimmedKey) {
|
|
3408
|
+
restoreEditor();
|
|
3409
|
+
this.showWarning("API key cannot be empty.");
|
|
3410
|
+
return;
|
|
3411
|
+
}
|
|
3412
|
+
this.session.modelRegistry.authStorage.set(providerId, trimmedName, {
|
|
3413
|
+
type: "api_key",
|
|
3414
|
+
key: trimmedKey,
|
|
3415
|
+
accountId: trimmedName,
|
|
3416
|
+
accountName: trimmedName,
|
|
3417
|
+
});
|
|
3418
|
+
this.session.modelRegistry.refresh();
|
|
3419
|
+
await this.updateAvailableProviderCount();
|
|
3420
|
+
restoreEditor();
|
|
3421
|
+
this.showStatus(`Saved API key for ${providerName}/${trimmedName}. Credentials saved to ${getAuthPath()}`);
|
|
3422
|
+
this.showModelSelector();
|
|
3423
|
+
}
|
|
3424
|
+
catch (error) {
|
|
3425
|
+
restoreEditor();
|
|
3426
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
3427
|
+
if (errorMsg !== "Login cancelled") {
|
|
3428
|
+
this.showError(`Failed to save API key for ${providerName}: ${errorMsg}`);
|
|
3429
|
+
}
|
|
3430
|
+
}
|
|
3431
|
+
}
|
|
3168
3432
|
// =========================================================================
|
|
3169
3433
|
// Command handlers
|
|
3170
3434
|
// =========================================================================
|