@mseep/obsidian-agent-client 0.10.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/.claude/hooks/gh-setup.sh +49 -0
- package/.claude/settings.json +15 -0
- package/.claude/skills/release-notes/SKILL.md +331 -0
- package/.editorconfig +10 -0
- package/.github/FUNDING.yml +2 -0
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -0
- package/.github/ISSUE_TEMPLATE/config.yml +11 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +59 -0
- package/.github/copilot-instructions.md +45 -0
- package/.github/pull_request_template.md +32 -0
- package/.github/workflows/ci.yaml +25 -0
- package/.github/workflows/docs.yml +58 -0
- package/.github/workflows/relay_to_openclaw.yml +59 -0
- package/.github/workflows/release.yaml +45 -0
- package/.prettierignore +10 -0
- package/.prettierrc +13 -0
- package/.vscode/extensions.json +7 -0
- package/.vscode/settings.json +37 -0
- package/.zed/settings.json +42 -0
- package/AGENTS.md +330 -0
- package/ARCHITECTURE.md +390 -0
- package/CONTRIBUTING.md +216 -0
- package/LICENSE +202 -0
- package/NOTICE +2 -0
- package/README.ja.md +121 -0
- package/README.md +125 -0
- package/docs/.vitepress/config.mts +124 -0
- package/docs/.vitepress/theme/custom.css +111 -0
- package/docs/.vitepress/theme/index.ts +4 -0
- package/docs/agent-setup/claude-code.md +84 -0
- package/docs/agent-setup/codex.md +76 -0
- package/docs/agent-setup/custom-agents.md +67 -0
- package/docs/agent-setup/gemini-cli.md +99 -0
- package/docs/agent-setup/index.md +34 -0
- package/docs/announcements/gemini-cli-deprecation.md +73 -0
- package/docs/getting-started/index.md +78 -0
- package/docs/getting-started/quick-start.md +38 -0
- package/docs/help/faq.md +181 -0
- package/docs/help/troubleshooting.md +221 -0
- package/docs/index.md +63 -0
- package/docs/public/apple-touch-icon.png +0 -0
- package/docs/public/demo.mp4 +0 -0
- package/docs/public/favicon-16x16.png +0 -0
- package/docs/public/favicon-32x32.png +0 -0
- package/docs/public/favicon.ico +0 -0
- package/docs/public/images/editing.webp +0 -0
- package/docs/public/images/export.webp +0 -0
- package/docs/public/images/floating-chat-button.webp +0 -0
- package/docs/public/images/floating-chat-instance-menu.webp +0 -0
- package/docs/public/images/floating-chat-view.webp +0 -0
- package/docs/public/images/mode-selection.webp +0 -0
- package/docs/public/images/model-selection.webp +0 -0
- package/docs/public/images/multi-session.webp +0 -0
- package/docs/public/images/remove-image.webp +0 -0
- package/docs/public/images/ribbon-icon.webp +0 -0
- package/docs/public/images/selection-context.gif +0 -0
- package/docs/public/images/sending-images.webp +0 -0
- package/docs/public/images/sending-messages.webp +0 -0
- package/docs/public/images/session-history-button.webp +0 -0
- package/docs/public/images/slash-commands-1.webp +0 -0
- package/docs/public/images/slash-commands-2.webp +0 -0
- package/docs/public/images/switch-agent.webp +0 -0
- package/docs/public/images/switch-default-agent.webp +0 -0
- package/docs/public/images/temporary-disable.gif +0 -0
- package/docs/reference/acp-support.md +110 -0
- package/docs/usage/chat-export.md +80 -0
- package/docs/usage/commands.md +51 -0
- package/docs/usage/context-files.md +57 -0
- package/docs/usage/editing.md +69 -0
- package/docs/usage/floating-chat.md +84 -0
- package/docs/usage/index.md +97 -0
- package/docs/usage/mcp-tools.md +33 -0
- package/docs/usage/mentions.md +70 -0
- package/docs/usage/mode-selection.md +28 -0
- package/docs/usage/model-selection.md +32 -0
- package/docs/usage/multi-session.md +68 -0
- package/docs/usage/sending-images.md +64 -0
- package/docs/usage/session-history.md +91 -0
- package/docs/usage/slash-commands.md +44 -0
- package/esbuild.config.mjs +49 -0
- package/eslint.config.mjs +25 -0
- package/main.js +228 -0
- package/manifest.json +11 -0
- package/package.json +52 -0
- package/src/acp/acp-client.ts +921 -0
- package/src/acp/acp-handler.ts +252 -0
- package/src/acp/permission-handler.ts +282 -0
- package/src/acp/terminal-handler.ts +264 -0
- package/src/acp/type-converter.ts +272 -0
- package/src/hooks/useAgent.ts +250 -0
- package/src/hooks/useAgentMessages.ts +470 -0
- package/src/hooks/useAgentSession.ts +544 -0
- package/src/hooks/useChatActions.ts +400 -0
- package/src/hooks/useHistoryModal.ts +219 -0
- package/src/hooks/useSessionHistory.ts +863 -0
- package/src/hooks/useSettings.ts +19 -0
- package/src/hooks/useSuggestions.ts +342 -0
- package/src/main.ts +9 -0
- package/src/plugin.ts +1126 -0
- package/src/services/chat-exporter.ts +552 -0
- package/src/services/message-sender.ts +755 -0
- package/src/services/message-state.ts +375 -0
- package/src/services/session-helpers.ts +211 -0
- package/src/services/session-state.ts +130 -0
- package/src/services/session-storage.ts +267 -0
- package/src/services/settings-normalizer.ts +255 -0
- package/src/services/settings-service.ts +285 -0
- package/src/services/update-checker.ts +128 -0
- package/src/services/vault-service.ts +558 -0
- package/src/services/view-registry.ts +345 -0
- package/src/types/agent.ts +92 -0
- package/src/types/chat.ts +351 -0
- package/src/types/errors.ts +136 -0
- package/src/types/obsidian-internals.d.ts +14 -0
- package/src/types/session.ts +731 -0
- package/src/ui/ChangeDirectoryModal.ts +137 -0
- package/src/ui/ChatContext.ts +25 -0
- package/src/ui/ChatHeader.tsx +295 -0
- package/src/ui/ChatPanel.tsx +1162 -0
- package/src/ui/ChatView.tsx +348 -0
- package/src/ui/ErrorBanner.tsx +104 -0
- package/src/ui/FloatingButton.tsx +351 -0
- package/src/ui/FloatingChatView.tsx +531 -0
- package/src/ui/InputArea.tsx +1107 -0
- package/src/ui/InputToolbar.tsx +371 -0
- package/src/ui/MessageBubble.tsx +442 -0
- package/src/ui/MessageList.tsx +265 -0
- package/src/ui/PermissionBanner.tsx +61 -0
- package/src/ui/SessionHistoryModal.tsx +821 -0
- package/src/ui/SettingsTab.ts +1337 -0
- package/src/ui/SuggestionPopup.tsx +138 -0
- package/src/ui/TerminalBlock.tsx +107 -0
- package/src/ui/ToolCallBlock.tsx +456 -0
- package/src/ui/shared/AttachmentStrip.tsx +57 -0
- package/src/ui/shared/IconButton.tsx +55 -0
- package/src/ui/shared/MarkdownRenderer.tsx +103 -0
- package/src/ui/view-host.ts +56 -0
- package/src/utils/error-utils.ts +274 -0
- package/src/utils/logger.ts +44 -0
- package/src/utils/mention-parser.ts +129 -0
- package/src/utils/paths.ts +246 -0
- package/src/utils/platform.ts +425 -0
- package/styles.css +2322 -0
- package/tsconfig.json +18 -0
- package/version-bump.mjs +18 -0
- package/versions.json +3 -0
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings Store Adapter
|
|
3
|
+
*
|
|
4
|
+
* Reactive settings store implementing ISettingAccess port.
|
|
5
|
+
* Manages plugin settings state with observer pattern for React integration
|
|
6
|
+
* via useSyncExternalStore, and handles persistence to Obsidian's data.json.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { AgentClientPluginSettings } from "../plugin";
|
|
10
|
+
import type AgentClientPlugin from "../plugin";
|
|
11
|
+
import type { ChatMessage } from "../types/chat";
|
|
12
|
+
import type { SavedSessionInfo } from "../types/session";
|
|
13
|
+
import { SessionStorage } from "./session-storage";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Port Types (from settings-access.port.ts)
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Interface for accessing and managing plugin settings.
|
|
21
|
+
*
|
|
22
|
+
* Provides reactive access to settings with subscription support
|
|
23
|
+
* for detecting changes (e.g., for React components using useSyncExternalStore).
|
|
24
|
+
*
|
|
25
|
+
* This port will be implemented by adapters that handle the actual
|
|
26
|
+
* storage mechanism (SettingsService, localStorage, etc.).
|
|
27
|
+
*/
|
|
28
|
+
export interface ISettingsAccess {
|
|
29
|
+
/**
|
|
30
|
+
* Get the current settings snapshot.
|
|
31
|
+
*
|
|
32
|
+
* Used by React's useSyncExternalStore to read current state.
|
|
33
|
+
* Should return the settings object immediately without side effects.
|
|
34
|
+
*
|
|
35
|
+
* @returns Current plugin settings
|
|
36
|
+
*/
|
|
37
|
+
getSnapshot(): AgentClientPluginSettings;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Update plugin settings.
|
|
41
|
+
*
|
|
42
|
+
* Merges the provided updates with existing settings and persists
|
|
43
|
+
* the changes. Notifies all subscribers after the update.
|
|
44
|
+
*
|
|
45
|
+
* @param updates - Partial settings object with properties to update
|
|
46
|
+
* @returns Promise that resolves when settings are saved
|
|
47
|
+
*/
|
|
48
|
+
updateSettings(updates: Partial<AgentClientPluginSettings>): Promise<void>;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Subscribe to settings changes.
|
|
52
|
+
*
|
|
53
|
+
* The listener will be called whenever settings are updated.
|
|
54
|
+
* Used by React's useSyncExternalStore to detect changes and trigger re-renders.
|
|
55
|
+
*
|
|
56
|
+
* @param listener - Callback to invoke on settings changes
|
|
57
|
+
* @returns Unsubscribe function to remove the listener
|
|
58
|
+
*/
|
|
59
|
+
subscribe(listener: () => void): () => void;
|
|
60
|
+
|
|
61
|
+
// ============================================================
|
|
62
|
+
// Session Storage Methods
|
|
63
|
+
// ============================================================
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Save a session to local storage.
|
|
67
|
+
*
|
|
68
|
+
* Updates existing session if sessionId matches.
|
|
69
|
+
* Maintains max 50 sessions, removing oldest when exceeded.
|
|
70
|
+
*
|
|
71
|
+
* @param info - Session metadata to save
|
|
72
|
+
* @returns Promise that resolves when session is saved
|
|
73
|
+
*/
|
|
74
|
+
saveSession(info: SavedSessionInfo): Promise<void>;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get saved sessions, optionally filtered by agentId and/or cwd.
|
|
78
|
+
*
|
|
79
|
+
* Returns sessions sorted by updatedAt (newest first).
|
|
80
|
+
*
|
|
81
|
+
* @param agentId - Optional filter by agent ID
|
|
82
|
+
* @param cwd - Optional filter by working directory
|
|
83
|
+
* @returns Array of saved session metadata
|
|
84
|
+
*/
|
|
85
|
+
getSavedSessions(agentId?: string, cwd?: string): SavedSessionInfo[];
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Delete a saved session by sessionId.
|
|
89
|
+
*
|
|
90
|
+
* @param sessionId - ID of session to delete
|
|
91
|
+
* @returns Promise that resolves when session is deleted
|
|
92
|
+
*/
|
|
93
|
+
deleteSession(sessionId: string): Promise<void>;
|
|
94
|
+
|
|
95
|
+
// ============================================================
|
|
96
|
+
// Session Message History Methods
|
|
97
|
+
// ============================================================
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Save message history for a session.
|
|
101
|
+
*
|
|
102
|
+
* Saves the full ChatMessage[] to a separate file in sessions/ directory.
|
|
103
|
+
* Overwrites existing file if present.
|
|
104
|
+
*
|
|
105
|
+
* @param sessionId - Session ID
|
|
106
|
+
* @param agentId - Agent ID for validation
|
|
107
|
+
* @param messages - Chat messages to save
|
|
108
|
+
* @returns Promise that resolves when messages are saved
|
|
109
|
+
*/
|
|
110
|
+
saveSessionMessages(
|
|
111
|
+
sessionId: string,
|
|
112
|
+
agentId: string,
|
|
113
|
+
messages: ChatMessage[],
|
|
114
|
+
): Promise<void>;
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Load message history for a session.
|
|
118
|
+
*
|
|
119
|
+
* Reads from sessions/{sessionId}.json file.
|
|
120
|
+
* Returns null if file doesn't exist.
|
|
121
|
+
*
|
|
122
|
+
* @param sessionId - Session ID
|
|
123
|
+
* @returns Promise that resolves with messages or null if not found
|
|
124
|
+
*/
|
|
125
|
+
loadSessionMessages(sessionId: string): Promise<ChatMessage[] | null>;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Delete message history file for a session.
|
|
129
|
+
*
|
|
130
|
+
* Called when session is deleted from savedSessions.
|
|
131
|
+
* Silently succeeds if file doesn't exist.
|
|
132
|
+
*
|
|
133
|
+
* @param sessionId - Session ID
|
|
134
|
+
* @returns Promise that resolves when file is deleted
|
|
135
|
+
*/
|
|
136
|
+
deleteSessionMessages(sessionId: string): Promise<void>;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Listener callback invoked when settings change */
|
|
140
|
+
type Listener = () => void;
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Observable store for plugin settings implementing ISettingsAccess port.
|
|
144
|
+
*
|
|
145
|
+
* Manages plugin settings state and notifies subscribers of changes.
|
|
146
|
+
* Designed to work with React's useSyncExternalStore hook for
|
|
147
|
+
* automatic re-rendering when settings update.
|
|
148
|
+
*
|
|
149
|
+
* Pattern: Observer/Publisher-Subscriber
|
|
150
|
+
*/
|
|
151
|
+
export class SettingsService implements ISettingsAccess {
|
|
152
|
+
/** Current settings state */
|
|
153
|
+
private state: AgentClientPluginSettings;
|
|
154
|
+
|
|
155
|
+
/** Set of registered listeners */
|
|
156
|
+
private listeners = new Set<Listener>();
|
|
157
|
+
|
|
158
|
+
/** Plugin instance for persistence */
|
|
159
|
+
private plugin: AgentClientPlugin;
|
|
160
|
+
|
|
161
|
+
/** Session storage delegate */
|
|
162
|
+
private sessionStorage: SessionStorage;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Create a new settings store.
|
|
166
|
+
*
|
|
167
|
+
* @param initial - Initial settings state
|
|
168
|
+
* @param plugin - Plugin instance for saving settings
|
|
169
|
+
*/
|
|
170
|
+
constructor(initial: AgentClientPluginSettings, plugin: AgentClientPlugin) {
|
|
171
|
+
this.state = initial;
|
|
172
|
+
this.plugin = plugin;
|
|
173
|
+
this.sessionStorage = new SessionStorage(plugin, this);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Get current settings snapshot.
|
|
178
|
+
*
|
|
179
|
+
* Used by React's useSyncExternalStore to read current state.
|
|
180
|
+
*
|
|
181
|
+
* @returns Current plugin settings
|
|
182
|
+
*/
|
|
183
|
+
getSnapshot = (): AgentClientPluginSettings => this.state;
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Update plugin settings.
|
|
187
|
+
*
|
|
188
|
+
* Merges the provided updates with existing settings, notifies subscribers,
|
|
189
|
+
* and persists changes to disk.
|
|
190
|
+
*
|
|
191
|
+
* @param updates - Partial settings object with properties to update
|
|
192
|
+
* @returns Promise that resolves when settings are saved
|
|
193
|
+
*/
|
|
194
|
+
async updateSettings(
|
|
195
|
+
updates: Partial<AgentClientPluginSettings>,
|
|
196
|
+
): Promise<void> {
|
|
197
|
+
const next = { ...this.state, ...updates };
|
|
198
|
+
this.state = next;
|
|
199
|
+
|
|
200
|
+
// Sync with plugin.settings (required for saveSettings to persist correctly)
|
|
201
|
+
this.plugin.settings = next;
|
|
202
|
+
|
|
203
|
+
// Notify all subscribers
|
|
204
|
+
for (const listener of this.listeners) {
|
|
205
|
+
listener();
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// Persist to disk
|
|
209
|
+
await this.plugin.saveSettings();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Subscribe to settings changes.
|
|
214
|
+
*
|
|
215
|
+
* The listener will be called whenever settings are updated via updateSettings().
|
|
216
|
+
* Used by React's useSyncExternalStore to detect changes.
|
|
217
|
+
*
|
|
218
|
+
* @param listener - Callback to invoke on settings changes
|
|
219
|
+
* @returns Unsubscribe function to remove the listener
|
|
220
|
+
*/
|
|
221
|
+
subscribe = (listener: Listener): (() => void) => {
|
|
222
|
+
this.listeners.add(listener);
|
|
223
|
+
return () => this.listeners.delete(listener);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Set entire settings object (legacy method).
|
|
228
|
+
*
|
|
229
|
+
* For backward compatibility with existing code.
|
|
230
|
+
* Delegates to updateSettings() for async persistence.
|
|
231
|
+
*
|
|
232
|
+
* @param next - New settings object
|
|
233
|
+
*/
|
|
234
|
+
set(next: AgentClientPluginSettings): void {
|
|
235
|
+
// Delegate to async updateSettings
|
|
236
|
+
// Note: Fire-and-forget - callers don't expect this to be async
|
|
237
|
+
void this.updateSettings(next);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// ============================================================
|
|
241
|
+
// Session Storage (delegated to SessionStorage)
|
|
242
|
+
// ============================================================
|
|
243
|
+
|
|
244
|
+
async saveSession(info: SavedSessionInfo): Promise<void> {
|
|
245
|
+
return this.sessionStorage.saveSession(info);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
getSavedSessions(agentId?: string, cwd?: string): SavedSessionInfo[] {
|
|
249
|
+
return this.sessionStorage.getSavedSessions(agentId, cwd);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async deleteSession(sessionId: string): Promise<void> {
|
|
253
|
+
return this.sessionStorage.deleteSession(sessionId);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
async saveSessionMessages(
|
|
257
|
+
sessionId: string,
|
|
258
|
+
agentId: string,
|
|
259
|
+
messages: ChatMessage[],
|
|
260
|
+
): Promise<void> {
|
|
261
|
+
return this.sessionStorage.saveSessionMessages(
|
|
262
|
+
sessionId,
|
|
263
|
+
agentId,
|
|
264
|
+
messages,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async loadSessionMessages(
|
|
269
|
+
sessionId: string,
|
|
270
|
+
): Promise<ChatMessage[] | null> {
|
|
271
|
+
return this.sessionStorage.loadSessionMessages(sessionId);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
async deleteSessionMessages(sessionId: string): Promise<void> {
|
|
275
|
+
return this.sessionStorage.deleteSessionMessages(sessionId);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Create a new settings store instance.
|
|
281
|
+
*/
|
|
282
|
+
export const createSettingsService = (
|
|
283
|
+
initial: AgentClientPluginSettings,
|
|
284
|
+
plugin: AgentClientPlugin,
|
|
285
|
+
) => new SettingsService(initial, plugin);
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent Update Checker
|
|
3
|
+
*
|
|
4
|
+
* Checks built-in agent ACP adapters for:
|
|
5
|
+
* 1. Package migration — deprecated packages that have been renamed
|
|
6
|
+
* 2. Version updates — newer versions available on npm
|
|
7
|
+
*
|
|
8
|
+
* Pure functions (non-React). Uses Obsidian's requestUrl for network access.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { requestUrl } from "obsidian";
|
|
12
|
+
import * as semver from "semver";
|
|
13
|
+
import type { OverlayVariant } from "../types/errors";
|
|
14
|
+
|
|
15
|
+
// ============================================================================
|
|
16
|
+
// Types
|
|
17
|
+
// ============================================================================
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Agent update notification to display in the UI.
|
|
21
|
+
* Compatible with ErrorInfo shape (title/message/suggestion).
|
|
22
|
+
*/
|
|
23
|
+
export interface AgentUpdateNotification {
|
|
24
|
+
/** Visual variant for the overlay */
|
|
25
|
+
variant: OverlayVariant;
|
|
26
|
+
/** Short notification title */
|
|
27
|
+
title: string;
|
|
28
|
+
/** Detailed notification message */
|
|
29
|
+
message: string;
|
|
30
|
+
/** Actionable suggestion (e.g., npm command) */
|
|
31
|
+
suggestion?: string;
|
|
32
|
+
/** Optional external link rendered as an actionable anchor (e.g. docs). */
|
|
33
|
+
link?: { text: string; url: string };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ============================================================================
|
|
37
|
+
// Known Packages
|
|
38
|
+
// ============================================================================
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Maps agentInfo.name → npm package name.
|
|
42
|
+
* Agents may report their name with or without the npm scope prefix,
|
|
43
|
+
* so we handle both forms.
|
|
44
|
+
*/
|
|
45
|
+
const KNOWN_AGENT_PACKAGES: Readonly<Record<string, string>> = {
|
|
46
|
+
"@agentclientprotocol/claude-agent-acp":
|
|
47
|
+
"@agentclientprotocol/claude-agent-acp",
|
|
48
|
+
"codex-acp": "@zed-industries/codex-acp",
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Deprecated agentInfo.name → replacement npm package name.
|
|
53
|
+
* Used to detect users still running old/renamed packages.
|
|
54
|
+
*/
|
|
55
|
+
const DEPRECATED_PACKAGES: Readonly<Record<string, string>> = {
|
|
56
|
+
"@zed-industries/claude-code-acp": "@agentclientprotocol/claude-agent-acp",
|
|
57
|
+
"@zed-industries/claude-agent-acp": "@agentclientprotocol/claude-agent-acp",
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// ============================================================================
|
|
61
|
+
// Public API
|
|
62
|
+
// ============================================================================
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Check if the agent needs a package migration or version update.
|
|
66
|
+
*
|
|
67
|
+
* Priority: migration notification > version update notification.
|
|
68
|
+
* - Migration is checked locally (no network) based on agentInfo.name.
|
|
69
|
+
* - Version update queries the npm registry.
|
|
70
|
+
*
|
|
71
|
+
* @returns AgentUpdateNotification if action needed, null otherwise.
|
|
72
|
+
*/
|
|
73
|
+
export async function checkAgentUpdate(agentInfo: {
|
|
74
|
+
name: string;
|
|
75
|
+
version?: string;
|
|
76
|
+
}): Promise<AgentUpdateNotification | null> {
|
|
77
|
+
// 1. Check for deprecated package (migration takes priority)
|
|
78
|
+
const replacement = DEPRECATED_PACKAGES[agentInfo.name];
|
|
79
|
+
if (replacement) {
|
|
80
|
+
return {
|
|
81
|
+
variant: "info",
|
|
82
|
+
title: "Package Migration Required",
|
|
83
|
+
message: `"${agentInfo.name}" has been renamed to "${replacement}".\nRun the following in your terminal:`,
|
|
84
|
+
suggestion: `npm uninstall -g ${agentInfo.name} && npm install -g ${replacement}`,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 2. Check for version update (known packages only)
|
|
89
|
+
const npmPackage = KNOWN_AGENT_PACKAGES[agentInfo.name];
|
|
90
|
+
if (!npmPackage || !agentInfo.version) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
const latestVersion = await fetchLatestVersion(npmPackage);
|
|
96
|
+
if (
|
|
97
|
+
latestVersion &&
|
|
98
|
+
semver.valid(agentInfo.version) &&
|
|
99
|
+
semver.gt(latestVersion, agentInfo.version)
|
|
100
|
+
) {
|
|
101
|
+
return {
|
|
102
|
+
variant: "info",
|
|
103
|
+
title: "Agent Update Available",
|
|
104
|
+
message: `${npmPackage}: ${agentInfo.version} → ${latestVersion}.\nRun the following in your terminal:`,
|
|
105
|
+
suggestion: `npm install -g ${npmPackage}@latest`,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
// Silently ignore network errors — update check is best-effort
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ============================================================================
|
|
116
|
+
// Internal
|
|
117
|
+
// ============================================================================
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Fetch the latest version of an npm package from the registry.
|
|
121
|
+
*/
|
|
122
|
+
async function fetchLatestVersion(packageName: string): Promise<string | null> {
|
|
123
|
+
const response = await requestUrl({
|
|
124
|
+
url: `https://registry.npmjs.org/${packageName}/latest`,
|
|
125
|
+
});
|
|
126
|
+
const data = response.json as { version?: string };
|
|
127
|
+
return data.version ? (semver.clean(data.version) ?? null) : null;
|
|
128
|
+
}
|