@pencil-agent/nano-pencil 1.11.26 → 1.11.28
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.
|
@@ -283,6 +283,10 @@ export interface ResourcesDiscoverResult {
|
|
|
283
283
|
export interface SessionStartEvent {
|
|
284
284
|
type: "session_start";
|
|
285
285
|
}
|
|
286
|
+
/** Fired when the interactive UI is fully ready to display extension output. */
|
|
287
|
+
export interface SessionReadyEvent {
|
|
288
|
+
type: "session_ready";
|
|
289
|
+
}
|
|
286
290
|
/** Fired before switching to another session (can be cancelled) */
|
|
287
291
|
export interface SessionBeforeSwitchEvent {
|
|
288
292
|
type: "session_before_switch";
|
|
@@ -351,7 +355,7 @@ export interface SessionTreeEvent {
|
|
|
351
355
|
summaryEntry?: BranchSummaryEntry;
|
|
352
356
|
fromExtension?: boolean;
|
|
353
357
|
}
|
|
354
|
-
export type SessionEvent = SessionStartEvent | SessionBeforeSwitchEvent | SessionSwitchEvent | SessionBeforeForkEvent | SessionForkEvent | SessionBeforeCompactEvent | SessionCompactEvent | SessionShutdownEvent | SessionBeforeTreeEvent | SessionTreeEvent;
|
|
358
|
+
export type SessionEvent = SessionStartEvent | SessionReadyEvent | SessionBeforeSwitchEvent | SessionSwitchEvent | SessionBeforeForkEvent | SessionForkEvent | SessionBeforeCompactEvent | SessionCompactEvent | SessionShutdownEvent | SessionBeforeTreeEvent | SessionTreeEvent;
|
|
355
359
|
/** Fired before each LLM call. Can modify messages. */
|
|
356
360
|
export interface ContextEvent {
|
|
357
361
|
type: "context";
|
|
@@ -652,6 +656,7 @@ export interface ExtensionAPI {
|
|
|
652
656
|
cwd: string;
|
|
653
657
|
on(event: "resources_discover", handler: ExtensionHandler<ResourcesDiscoverEvent, ResourcesDiscoverResult>): void;
|
|
654
658
|
on(event: "session_start", handler: ExtensionHandler<SessionStartEvent>): void;
|
|
659
|
+
on(event: "session_ready", handler: ExtensionHandler<SessionReadyEvent>): void;
|
|
655
660
|
on(event: "session_before_switch", handler: ExtensionHandler<SessionBeforeSwitchEvent, SessionBeforeSwitchResult>): void;
|
|
656
661
|
on(event: "session_switch", handler: ExtensionHandler<SessionSwitchEvent>): void;
|
|
657
662
|
on(event: "session_before_fork", handler: ExtensionHandler<SessionBeforeForkEvent, SessionBeforeForkResult>): void;
|
|
@@ -272,6 +272,7 @@ export class ModelSelectorComponent extends Container {
|
|
|
272
272
|
"dashscope-coding": "DashScope API key (sk-sp-...)",
|
|
273
273
|
"qianfan-coding": "Qianfan API key",
|
|
274
274
|
"ark-coding": "Ark API key",
|
|
275
|
+
openrouter: "OpenRouter API key",
|
|
275
276
|
};
|
|
276
277
|
const prompt = providerHints[model.provider] ?? `${model.provider} API key`;
|
|
277
278
|
const key = await promptForApiKey({ prompt: `Enter ${prompt}` });
|
|
@@ -293,6 +293,10 @@ export declare class InteractiveMode {
|
|
|
293
293
|
* Handle /apikey command - allow user to update API key for current provider
|
|
294
294
|
*/
|
|
295
295
|
private handleApiKeyCommand;
|
|
296
|
+
private getStoredApiKey;
|
|
297
|
+
private resolveProviderId;
|
|
298
|
+
private promptForProviderApiKey;
|
|
299
|
+
private handleLoginCommand;
|
|
296
300
|
private handleProviderCredentialsCommand;
|
|
297
301
|
private ensureProviderConfiguredForSelection;
|
|
298
302
|
private configureCustomProtocolProvider;
|
|
@@ -331,6 +331,7 @@ export class InteractiveMode {
|
|
|
331
331
|
this.subscribeToAgent();
|
|
332
332
|
this.chatContainer.clear();
|
|
333
333
|
this.renderInitialMessages();
|
|
334
|
+
await this.session.extensionRunner?.emit({ type: "session_ready" });
|
|
334
335
|
// Set up theme file watcher
|
|
335
336
|
onThemeChange(() => {
|
|
336
337
|
this.ui.invalidate();
|
|
@@ -1759,8 +1760,8 @@ export class InteractiveMode {
|
|
|
1759
1760
|
this.editor.setText("");
|
|
1760
1761
|
return;
|
|
1761
1762
|
}
|
|
1762
|
-
if (text === "/login") {
|
|
1763
|
-
this.
|
|
1763
|
+
if (text === "/login" || text.startsWith("/login ")) {
|
|
1764
|
+
await this.handleLoginCommand(text);
|
|
1764
1765
|
this.editor.setText("");
|
|
1765
1766
|
return;
|
|
1766
1767
|
}
|
|
@@ -3078,6 +3079,66 @@ export class InteractiveMode {
|
|
|
3078
3079
|
async handleApiKeyCommand() {
|
|
3079
3080
|
await this.handleProviderCredentialsCommand();
|
|
3080
3081
|
}
|
|
3082
|
+
getStoredApiKey(provider) {
|
|
3083
|
+
const credential = this.session.modelRegistry.authStorage.get(provider);
|
|
3084
|
+
return credential?.type === "api_key" ? credential.key : undefined;
|
|
3085
|
+
}
|
|
3086
|
+
resolveProviderId(input) {
|
|
3087
|
+
const normalized = input.trim().toLowerCase();
|
|
3088
|
+
if (!normalized)
|
|
3089
|
+
return undefined;
|
|
3090
|
+
const providerMap = new Map();
|
|
3091
|
+
for (const model of this.session.modelRegistry.getAll()) {
|
|
3092
|
+
providerMap.set(model.provider.toLowerCase(), model.provider);
|
|
3093
|
+
}
|
|
3094
|
+
for (const provider of getOAuthProviders()) {
|
|
3095
|
+
providerMap.set(provider.id.toLowerCase(), provider.id);
|
|
3096
|
+
}
|
|
3097
|
+
return providerMap.get(normalized);
|
|
3098
|
+
}
|
|
3099
|
+
async promptForProviderApiKey(provider, options = {}) {
|
|
3100
|
+
const currentApiKey = this.getStoredApiKey(provider);
|
|
3101
|
+
const title = options.title ?? `Update API key for ${provider}`;
|
|
3102
|
+
const apiKey = await this.showExtensionInput(title, "API key", {
|
|
3103
|
+
initialValue: currentApiKey,
|
|
3104
|
+
});
|
|
3105
|
+
if (apiKey === undefined) {
|
|
3106
|
+
this.showStatus("Configuration cancelled");
|
|
3107
|
+
return false;
|
|
3108
|
+
}
|
|
3109
|
+
const trimmedApiKey = apiKey.trim();
|
|
3110
|
+
if (!trimmedApiKey) {
|
|
3111
|
+
this.showStatus("Configuration cancelled");
|
|
3112
|
+
return false;
|
|
3113
|
+
}
|
|
3114
|
+
this.session.modelRegistry.authStorage.set(provider, {
|
|
3115
|
+
type: "api_key",
|
|
3116
|
+
key: trimmedApiKey,
|
|
3117
|
+
});
|
|
3118
|
+
this.session.modelRegistry.refresh();
|
|
3119
|
+
this.showStatus(`Updated API key for ${provider}`);
|
|
3120
|
+
return true;
|
|
3121
|
+
}
|
|
3122
|
+
async handleLoginCommand(text) {
|
|
3123
|
+
const rawProvider = text.startsWith("/login ") ? text.slice(7).trim() : "";
|
|
3124
|
+
if (!rawProvider) {
|
|
3125
|
+
this.showOAuthSelector("login");
|
|
3126
|
+
return;
|
|
3127
|
+
}
|
|
3128
|
+
const providerId = this.resolveProviderId(rawProvider);
|
|
3129
|
+
if (!providerId) {
|
|
3130
|
+
this.showError(`Unknown provider: ${rawProvider}`);
|
|
3131
|
+
return;
|
|
3132
|
+
}
|
|
3133
|
+
const oauthProvider = getOAuthProviders().find((provider) => provider.id === providerId);
|
|
3134
|
+
if (oauthProvider) {
|
|
3135
|
+
await this.showLoginDialog(oauthProvider.id);
|
|
3136
|
+
return;
|
|
3137
|
+
}
|
|
3138
|
+
await this.promptForProviderApiKey(providerId, {
|
|
3139
|
+
title: `Set API key for ${providerId}`,
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
3081
3142
|
async handleProviderCredentialsCommand() {
|
|
3082
3143
|
const currentModel = this.session.model;
|
|
3083
3144
|
if (!currentModel) {
|
|
@@ -3095,22 +3156,7 @@ export class InteractiveMode {
|
|
|
3095
3156
|
}
|
|
3096
3157
|
return;
|
|
3097
3158
|
}
|
|
3098
|
-
|
|
3099
|
-
if (apiKey === undefined) {
|
|
3100
|
-
this.showStatus("Configuration cancelled");
|
|
3101
|
-
return;
|
|
3102
|
-
}
|
|
3103
|
-
const trimmedApiKey = apiKey.trim();
|
|
3104
|
-
if (!trimmedApiKey) {
|
|
3105
|
-
this.showStatus("Configuration cancelled");
|
|
3106
|
-
return;
|
|
3107
|
-
}
|
|
3108
|
-
this.session.modelRegistry.authStorage.set(provider, {
|
|
3109
|
-
type: "api_key",
|
|
3110
|
-
key: trimmedApiKey,
|
|
3111
|
-
});
|
|
3112
|
-
this.session.modelRegistry.refresh();
|
|
3113
|
-
this.showStatus(`Updated API key for ${provider}`);
|
|
3159
|
+
await this.promptForProviderApiKey(provider);
|
|
3114
3160
|
}
|
|
3115
3161
|
catch (error) {
|
|
3116
3162
|
this.showError(error instanceof Error ? error.message : String(error));
|
|
@@ -3130,6 +3176,7 @@ export class InteractiveMode {
|
|
|
3130
3176
|
definition.defaultBaseUrl;
|
|
3131
3177
|
const currentModelName = getCustomProtocolProviderModelName(modelsPath, provider) ??
|
|
3132
3178
|
"custom-model";
|
|
3179
|
+
const currentApiKey = this.getStoredApiKey(provider) ?? "";
|
|
3133
3180
|
const hasExistingApiKey = authStorage.has(provider);
|
|
3134
3181
|
if (!options.force &&
|
|
3135
3182
|
hasExistingApiKey &&
|
|
@@ -3148,7 +3195,7 @@ export class InteractiveMode {
|
|
|
3148
3195
|
}
|
|
3149
3196
|
const apiKeyInput = await this.showExtensionInput(`${definition.label} API key`, hasExistingApiKey && options.force
|
|
3150
3197
|
? "Leave empty to keep the current API key"
|
|
3151
|
-
: "API key");
|
|
3198
|
+
: "API key", { initialValue: currentApiKey });
|
|
3152
3199
|
if (apiKeyInput === undefined) {
|
|
3153
3200
|
return false;
|
|
3154
3201
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pencil-agent/nano-pencil",
|
|
3
|
-
"version": "1.11.
|
|
3
|
+
"version": "1.11.28",
|
|
4
4
|
"description": "CLI writing agent with read, bash, edit, write tools and session management. Based on pi; supports DashScope Coding Plan. Soul enabled by default for AI personality evolution.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|