@mem9/opencode 0.1.1 → 0.1.3
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/README.md +64 -101
- package/package.json +2 -5
- package/src/shared/setup-files.ts +19 -0
- package/src/tui/index.ts +110 -43
package/README.md
CHANGED
|
@@ -2,82 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
Persistent memory for [OpenCode](https://opencode.ai).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package has two entrypoints:
|
|
6
6
|
|
|
7
7
|
- a server plugin for recall, auto-ingest, and memory tools
|
|
8
8
|
- a TUI plugin for interactive setup inside OpenCode
|
|
9
9
|
|
|
10
10
|
## Quick Start
|
|
11
11
|
|
|
12
|
-
### 1. Install
|
|
12
|
+
### 1. Install mem9 once at user scope
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
User scope:
|
|
17
|
-
|
|
18
|
-
File: `~/.config/opencode/opencode.json`
|
|
19
|
-
|
|
20
|
-
```json
|
|
21
|
-
{
|
|
22
|
-
"plugin": ["@mem9/opencode"]
|
|
23
|
-
}
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Project scope:
|
|
27
|
-
|
|
28
|
-
File: `<project>/.opencode/opencode.json`
|
|
29
|
-
|
|
30
|
-
```json
|
|
31
|
-
{
|
|
32
|
-
"plugin": ["@mem9/opencode"]
|
|
33
|
-
}
|
|
34
|
-
```
|
|
35
|
-
|
|
36
|
-
Recommended pattern:
|
|
37
|
-
|
|
38
|
-
- install the server plugin once at user scope
|
|
39
|
-
- keep project-specific behavior in `<project>/.opencode/mem9.json`
|
|
40
|
-
|
|
41
|
-
`@mem9/opencode` resolves npm `latest`.
|
|
42
|
-
If you publish a prerelease channel, install it with an explicit npm specifier instead:
|
|
43
|
-
|
|
44
|
-
```json
|
|
45
|
-
{
|
|
46
|
-
"plugin": ["@mem9/opencode@rc"]
|
|
47
|
-
}
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
You can also pin an exact prerelease version:
|
|
51
|
-
|
|
52
|
-
```json
|
|
53
|
-
{
|
|
54
|
-
"plugin": ["@mem9/opencode@0.1.0-rc.0"]
|
|
55
|
-
}
|
|
14
|
+
```bash
|
|
15
|
+
opencode plugin --global @mem9/opencode
|
|
56
16
|
```
|
|
57
17
|
|
|
58
|
-
|
|
18
|
+
That adds `@mem9/opencode` to these files:
|
|
59
19
|
|
|
60
|
-
|
|
61
|
-
- npm plugin plus local file plugin
|
|
20
|
+
macOS/Linux:
|
|
62
21
|
|
|
63
|
-
|
|
22
|
+
- `~/.config/opencode/opencode.json`
|
|
23
|
+
- `~/.config/opencode/tui.json`
|
|
64
24
|
|
|
65
|
-
|
|
25
|
+
Windows:
|
|
66
26
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
"plugin": ["@mem9/opencode"]
|
|
70
|
-
}
|
|
71
|
-
```
|
|
27
|
+
- `%APPDATA%\\opencode\\opencode.json`
|
|
28
|
+
- `%APPDATA%\\opencode\\tui.json`
|
|
72
29
|
|
|
73
|
-
|
|
30
|
+
mem9 works best with one global plugin install plus project `mem9.json` overrides.
|
|
31
|
+
OpenCode merges plugin lists across scopes, so one install keeps recall, ingest, and tools predictable.
|
|
74
32
|
|
|
75
|
-
###
|
|
33
|
+
### 2. Restart OpenCode and run `/mem9-setup`
|
|
76
34
|
|
|
77
|
-
`/mem9-setup` is the
|
|
35
|
+
`/mem9-setup` is the main entrypoint for:
|
|
78
36
|
|
|
79
37
|
- shared mem9 credentials
|
|
80
|
-
- OpenCode
|
|
38
|
+
- OpenCode mem9 settings
|
|
81
39
|
|
|
82
40
|
When no usable profile exists, it shows two actions:
|
|
83
41
|
|
|
@@ -89,32 +47,45 @@ When usable profiles already exist, it shows four actions:
|
|
|
89
47
|
- `Get a mem9 API key automatically`
|
|
90
48
|
- `Add an existing mem9 API key`
|
|
91
49
|
- `Use an existing mem9 profile in a scope`
|
|
92
|
-
- `
|
|
50
|
+
- `Adjust scope settings`
|
|
93
51
|
|
|
94
|
-
|
|
52
|
+
The last two actions do different things:
|
|
95
53
|
|
|
96
|
-
|
|
54
|
+
- `Use an existing mem9 profile in a scope` changes which profile a user or project scope uses
|
|
55
|
+
- `Adjust scope settings` changes `debug`, `defaultTimeoutMs`, and `searchTimeoutMs` for a user or project scope
|
|
97
56
|
|
|
98
|
-
|
|
57
|
+
### 3. Add project overrides only when you need them
|
|
99
58
|
|
|
100
|
-
|
|
101
|
-
|
|
59
|
+
Keep the plugin install global.
|
|
60
|
+
Use `<project>/.opencode/mem9.json` when one repository needs a different profile, debug flag, or timeout.
|
|
102
61
|
|
|
103
|
-
|
|
62
|
+
Example:
|
|
104
63
|
|
|
105
|
-
|
|
106
|
-
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"schemaVersion": 1,
|
|
67
|
+
"profileId": "default",
|
|
68
|
+
"debug": false,
|
|
69
|
+
"defaultTimeoutMs": 8000,
|
|
70
|
+
"searchTimeoutMs": 15000
|
|
71
|
+
}
|
|
72
|
+
```
|
|
107
73
|
|
|
108
|
-
mem9
|
|
74
|
+
## Where mem9 stores data
|
|
109
75
|
|
|
110
|
-
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
-
|
|
114
|
-
|
|
115
|
-
|
|
76
|
+
- Shared credentials:
|
|
77
|
+
macOS/Linux: `$HOME/.mem9/.credentials.json`
|
|
78
|
+
Windows: `%USERPROFILE%\\.mem9\\.credentials.json`
|
|
79
|
+
- Global mem9 config:
|
|
80
|
+
macOS/Linux: `~/.config/opencode/mem9.json`
|
|
81
|
+
Windows: `%APPDATA%\\opencode\\mem9.json`
|
|
82
|
+
- Project mem9 override:
|
|
83
|
+
all platforms: `<project>/.opencode/mem9.json`
|
|
84
|
+
- Debug logs:
|
|
85
|
+
macOS/Linux: `~/.local/share/opencode/plugins/mem9/log/YYYY-MM-DD.jsonl`
|
|
86
|
+
Windows: `%LOCALAPPDATA%\\opencode\\plugins\\mem9\\log\\YYYY-MM-DD.jsonl`
|
|
116
87
|
|
|
117
|
-
`MEM9_HOME` defaults to `$HOME/.mem9
|
|
88
|
+
`MEM9_HOME` defaults to `$HOME/.mem9` on macOS/Linux and `%USERPROFILE%\\.mem9` on Windows.
|
|
118
89
|
|
|
119
90
|
## Credentials File
|
|
120
91
|
|
|
@@ -173,33 +144,33 @@ These environment variables still override disk config at runtime:
|
|
|
173
144
|
- `MEM9_DEBUG`
|
|
174
145
|
- `MEM9_HOME`
|
|
175
146
|
|
|
176
|
-
Legacy compatibility
|
|
147
|
+
Legacy compatibility:
|
|
177
148
|
|
|
178
149
|
- `MEM9_TENANT_ID`
|
|
179
150
|
|
|
180
151
|
`MEM9_TENANT_ID` is treated as the API key source for older setups.
|
|
181
152
|
|
|
182
|
-
##
|
|
153
|
+
## Upgrading
|
|
183
154
|
|
|
184
|
-
|
|
155
|
+
OpenCode caches npm plugins by package specifier.
|
|
156
|
+
When config points at `@mem9/opencode`, OpenCode resolves it as `@mem9/opencode@latest`.
|
|
185
157
|
|
|
186
|
-
|
|
158
|
+
Upgrade flow:
|
|
187
159
|
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
160
|
+
1. Quit OpenCode.
|
|
161
|
+
2. Delete the cached folder that matches the installed specifier.
|
|
162
|
+
macOS/Linux default: `~/.cache/opencode/packages/@mem9/opencode@latest`
|
|
163
|
+
Windows default: `%LOCALAPPDATA%\\opencode\\packages\\@mem9\\opencode@latest`
|
|
164
|
+
If you pinned an exact version such as `@mem9/opencode@0.1.1`, delete that exact folder name instead.
|
|
165
|
+
3. Run:
|
|
193
166
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
```json
|
|
197
|
-
{
|
|
198
|
-
"plugin": ["./.opencode/plugins/mem9/src/tui/index.ts"]
|
|
199
|
-
}
|
|
167
|
+
```bash
|
|
168
|
+
opencode plugin --force --global @mem9/opencode
|
|
200
169
|
```
|
|
201
170
|
|
|
202
|
-
|
|
171
|
+
4. Restart OpenCode.
|
|
172
|
+
|
|
173
|
+
For prerelease testing, install an explicit npm specifier such as `@mem9/opencode@rc` or an exact version.
|
|
203
174
|
|
|
204
175
|
## What the Plugin Does
|
|
205
176
|
|
|
@@ -211,7 +182,7 @@ The server plugin does three things:
|
|
|
211
182
|
|
|
212
183
|
### Hook Flow
|
|
213
184
|
|
|
214
|
-
OpenCode integration
|
|
185
|
+
OpenCode integration uses four runtime hooks:
|
|
215
186
|
|
|
216
187
|
| Hook | What mem9 does |
|
|
217
188
|
| --- | --- |
|
|
@@ -253,16 +224,8 @@ The plugin registers these tools:
|
|
|
253
224
|
## Troubleshooting
|
|
254
225
|
|
|
255
226
|
- `Setup pending` means the plugin could not find a usable runtime identity. Run `/mem9-setup`, add `MEM9_API_KEY`, or point the active `mem9.json` scope at a profile with a non-empty `apiKey`.
|
|
256
|
-
- If `/mem9-setup` is missing, confirm `@mem9/opencode` is listed in
|
|
227
|
+
- If `/mem9-setup` is missing, confirm `@mem9/opencode` is listed in your global `tui.json`.
|
|
257
228
|
- If recall or tools work in one project and not another, check whether the project has its own `.opencode/mem9.json` override.
|
|
258
229
|
- If recall, auto-ingest, or debug logs appear to run twice, check for duplicate plugin registration across user scope, project scope, npm, or local file paths.
|
|
259
230
|
- If the selected profile exists but has no `apiKey`, update that profile in `$MEM9_HOME/.credentials.json`.
|
|
260
231
|
- If debug logging is enabled and no file appears, confirm OpenCode can write to its state directory.
|
|
261
|
-
|
|
262
|
-
## Local Verification
|
|
263
|
-
|
|
264
|
-
```bash
|
|
265
|
-
pnpm test
|
|
266
|
-
pnpm run typecheck
|
|
267
|
-
pnpm run pack:check
|
|
268
|
-
```
|
package/package.json
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mem9/opencode",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "OpenCode memory plugin — cloud-persistent AI agent memory with hybrid vector + keyword search via mem9",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
|
-
"oc-plugin": [
|
|
8
|
-
"server",
|
|
9
|
-
"tui"
|
|
10
|
-
],
|
|
11
7
|
"exports": {
|
|
12
8
|
".": "./src/index.ts",
|
|
9
|
+
"./server": "./src/index.ts",
|
|
13
10
|
"./tui": "./src/tui/index.ts"
|
|
14
11
|
},
|
|
15
12
|
"license": "Apache-2.0",
|
|
@@ -17,6 +17,7 @@ export interface SetupProfileSummary {
|
|
|
17
17
|
label: string;
|
|
18
18
|
baseUrl: string;
|
|
19
19
|
hasApiKey: boolean;
|
|
20
|
+
apiKeyPreview: string;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface ScopeConfigState {
|
|
@@ -112,6 +113,23 @@ function hasApiKey(profile: unknown): boolean {
|
|
|
112
113
|
return Boolean(normalizeOptionalString(profile.apiKey));
|
|
113
114
|
}
|
|
114
115
|
|
|
116
|
+
function summarizeApiKeyPreview(apiKey: string): string {
|
|
117
|
+
const normalized = apiKey.trim();
|
|
118
|
+
if (normalized.length === 0) {
|
|
119
|
+
return "";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (normalized.length <= 4) {
|
|
123
|
+
return `${normalized.slice(0, 1)}...`;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (normalized.length <= 8) {
|
|
127
|
+
return `${normalized.slice(0, 2)}...${normalized.slice(-2)}`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return `${normalized.slice(0, 4)}...${normalized.slice(-4)}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
115
133
|
function normalizeProfileRecord(
|
|
116
134
|
profileId: string,
|
|
117
135
|
profile: Mem9Profile | undefined,
|
|
@@ -188,6 +206,7 @@ function buildProfiles(
|
|
|
188
206
|
label: normalized.label,
|
|
189
207
|
baseUrl: normalized.baseUrl,
|
|
190
208
|
hasApiKey: normalized.apiKey.trim().length > 0,
|
|
209
|
+
apiKeyPreview: summarizeApiKeyPreview(normalized.apiKey),
|
|
191
210
|
};
|
|
192
211
|
})
|
|
193
212
|
.sort(sortProfileSummaries);
|
package/src/tui/index.ts
CHANGED
|
@@ -3,6 +3,7 @@ import type {
|
|
|
3
3
|
TuiCommand,
|
|
4
4
|
TuiPlugin,
|
|
5
5
|
TuiPluginApi,
|
|
6
|
+
TuiToast,
|
|
6
7
|
} from "@opencode-ai/plugin/tui";
|
|
7
8
|
import {
|
|
8
9
|
resolveMem9Home,
|
|
@@ -27,7 +28,7 @@ type SetupAction =
|
|
|
27
28
|
| "use-profile-in-scope"
|
|
28
29
|
| "configure-scope";
|
|
29
30
|
|
|
30
|
-
type ScopeFlowMode = "profile-only" | "
|
|
31
|
+
type ScopeFlowMode = "profile-only" | "settings-only";
|
|
31
32
|
|
|
32
33
|
interface ProfileDraft {
|
|
33
34
|
profileId: string;
|
|
@@ -50,6 +51,33 @@ export interface SetupActionOption {
|
|
|
50
51
|
description: string;
|
|
51
52
|
}
|
|
52
53
|
|
|
54
|
+
export interface SetupProfileOption {
|
|
55
|
+
title: string;
|
|
56
|
+
value: string;
|
|
57
|
+
description: string;
|
|
58
|
+
disabled: boolean;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
interface SetupProfileOptionState {
|
|
62
|
+
currentProfileId?: string;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let hasShownVisibleApiKeyWarning = false;
|
|
66
|
+
const DEFAULT_TOAST_DURATION_MS = 5000;
|
|
67
|
+
|
|
68
|
+
function scheduleDialogTransition(next: () => void): void {
|
|
69
|
+
// Delay prompt-to-prompt transitions so the next dialog does not reuse
|
|
70
|
+
// the same Enter keypress that confirmed the current prompt.
|
|
71
|
+
setTimeout(next, 0);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function showToast(api: TuiPluginApi, toast: TuiToast): void {
|
|
75
|
+
api.ui.toast({
|
|
76
|
+
duration: toast.duration ?? DEFAULT_TOAST_DURATION_MS,
|
|
77
|
+
...toast,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
53
81
|
function getProjectDir(api: TuiPluginApi): string {
|
|
54
82
|
const worktree = api.state.path.worktree.trim();
|
|
55
83
|
if (worktree.length > 0) {
|
|
@@ -102,7 +130,7 @@ function showError(
|
|
|
102
130
|
retryCommand = false,
|
|
103
131
|
): void {
|
|
104
132
|
const suffix = retryCommand ? " Run /mem9-setup again when you are ready to retry." : "";
|
|
105
|
-
api
|
|
133
|
+
showToast(api, {
|
|
106
134
|
variant: "error",
|
|
107
135
|
title: "mem9 setup failed",
|
|
108
136
|
message: `${error instanceof Error ? error.message : String(error)}${suffix}`,
|
|
@@ -110,7 +138,7 @@ function showError(
|
|
|
110
138
|
}
|
|
111
139
|
|
|
112
140
|
function showProfileSavedSuccess(api: TuiPluginApi, profileId: string): void {
|
|
113
|
-
api
|
|
141
|
+
showToast(api, {
|
|
114
142
|
variant: "success",
|
|
115
143
|
title: "mem9 configured",
|
|
116
144
|
message: `Saved profile ${profileId} and set it as the default user profile. Restart OpenCode to reload mem9.`,
|
|
@@ -123,7 +151,7 @@ function showScopeSavedSuccess(
|
|
|
123
151
|
profileId: string,
|
|
124
152
|
): void {
|
|
125
153
|
const scopeLabel = scope === "user" ? "user settings" : "project settings";
|
|
126
|
-
api
|
|
154
|
+
showToast(api, {
|
|
127
155
|
variant: "success",
|
|
128
156
|
title: "mem9 configured",
|
|
129
157
|
message: `Saved ${scopeLabel} with profile ${profileId}. Restart OpenCode to reload mem9.`,
|
|
@@ -155,12 +183,12 @@ export function buildSetupActionOptions(
|
|
|
155
183
|
{
|
|
156
184
|
title: "Use an existing mem9 profile in a scope",
|
|
157
185
|
value: "use-profile-in-scope",
|
|
158
|
-
description: "Choose
|
|
186
|
+
description: "Choose which saved profile user or project settings should use.",
|
|
159
187
|
},
|
|
160
188
|
{
|
|
161
|
-
title: "
|
|
189
|
+
title: "Adjust scope settings",
|
|
162
190
|
value: "configure-scope",
|
|
163
|
-
description: "Change
|
|
191
|
+
description: "Change debug logging, request timeouts, and other mem9 settings for a user or project scope.",
|
|
164
192
|
},
|
|
165
193
|
);
|
|
166
194
|
}
|
|
@@ -168,6 +196,30 @@ export function buildSetupActionOptions(
|
|
|
168
196
|
return options;
|
|
169
197
|
}
|
|
170
198
|
|
|
199
|
+
function formatProfileTitle(profile: SetupProfileSummary): string {
|
|
200
|
+
return profile.label === profile.profileId
|
|
201
|
+
? profile.label
|
|
202
|
+
: `${profile.label} (${profile.profileId})`;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function buildScopeProfileOptions(
|
|
206
|
+
state: Pick<SetupState, "profiles">,
|
|
207
|
+
optionState: SetupProfileOptionState = {},
|
|
208
|
+
): SetupProfileOption[] {
|
|
209
|
+
return state.profiles.map((profile) => ({
|
|
210
|
+
title: formatProfileTitle(profile),
|
|
211
|
+
value: profile.profileId,
|
|
212
|
+
description: [
|
|
213
|
+
profile.apiKeyPreview || "API key missing",
|
|
214
|
+
profile.baseUrl,
|
|
215
|
+
profile.profileId === optionState.currentProfileId ? "Current in this scope" : undefined,
|
|
216
|
+
]
|
|
217
|
+
.filter((value): value is string => Boolean(value))
|
|
218
|
+
.join(" | "),
|
|
219
|
+
disabled: !profile.hasApiKey,
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
222
|
+
|
|
171
223
|
function parsePositiveInteger(value: string, field: string): number | null {
|
|
172
224
|
const trimmed = value.trim();
|
|
173
225
|
if (trimmed.length === 0) {
|
|
@@ -279,7 +331,7 @@ function showActionDialog(
|
|
|
279
331
|
return;
|
|
280
332
|
}
|
|
281
333
|
|
|
282
|
-
showScopeDialog(api, paths, state, "
|
|
334
|
+
showScopeDialog(api, paths, state, "settings-only");
|
|
283
335
|
},
|
|
284
336
|
}),
|
|
285
337
|
);
|
|
@@ -300,7 +352,7 @@ function showProfileIdDialog(
|
|
|
300
352
|
onConfirm: (value) => {
|
|
301
353
|
const next = value.trim();
|
|
302
354
|
if (next.length === 0) {
|
|
303
|
-
api
|
|
355
|
+
showToast(api, {
|
|
304
356
|
variant: "warning",
|
|
305
357
|
message: "Profile ID is required.",
|
|
306
358
|
});
|
|
@@ -308,7 +360,7 @@ function showProfileIdDialog(
|
|
|
308
360
|
}
|
|
309
361
|
|
|
310
362
|
if (isReusableProfileID(state, next)) {
|
|
311
|
-
api
|
|
363
|
+
showToast(api, {
|
|
312
364
|
variant: "warning",
|
|
313
365
|
message: "That profile already has credentials. Pick a new profile ID or use the existing profile in scope settings.",
|
|
314
366
|
});
|
|
@@ -319,7 +371,9 @@ function showProfileIdDialog(
|
|
|
319
371
|
if (draft.label.trim().length === 0) {
|
|
320
372
|
draft.label = next === "default" ? "Personal" : next;
|
|
321
373
|
}
|
|
322
|
-
|
|
374
|
+
scheduleDialogTransition(() => {
|
|
375
|
+
showProfileLabelDialog(api, paths, state, action, draft);
|
|
376
|
+
});
|
|
323
377
|
},
|
|
324
378
|
onCancel: () => {
|
|
325
379
|
showActionDialog(api, paths, state);
|
|
@@ -343,7 +397,7 @@ function showProfileLabelDialog(
|
|
|
343
397
|
onConfirm: (value) => {
|
|
344
398
|
const next = value.trim();
|
|
345
399
|
if (next.length === 0) {
|
|
346
|
-
api
|
|
400
|
+
showToast(api, {
|
|
347
401
|
variant: "warning",
|
|
348
402
|
message: "Profile label is required.",
|
|
349
403
|
});
|
|
@@ -351,10 +405,14 @@ function showProfileLabelDialog(
|
|
|
351
405
|
}
|
|
352
406
|
|
|
353
407
|
draft.label = next;
|
|
354
|
-
|
|
408
|
+
scheduleDialogTransition(() => {
|
|
409
|
+
showProfileBaseUrlDialog(api, paths, state, action, draft);
|
|
410
|
+
});
|
|
355
411
|
},
|
|
356
412
|
onCancel: () => {
|
|
357
|
-
|
|
413
|
+
scheduleDialogTransition(() => {
|
|
414
|
+
showProfileIdDialog(api, paths, state, action, draft);
|
|
415
|
+
});
|
|
358
416
|
},
|
|
359
417
|
}),
|
|
360
418
|
);
|
|
@@ -375,7 +433,7 @@ function showProfileBaseUrlDialog(
|
|
|
375
433
|
onConfirm: (value) => {
|
|
376
434
|
const next = value.trim();
|
|
377
435
|
if (next.length === 0) {
|
|
378
|
-
api
|
|
436
|
+
showToast(api, {
|
|
379
437
|
variant: "warning",
|
|
380
438
|
message: "mem9 API URL is required.",
|
|
381
439
|
});
|
|
@@ -388,10 +446,14 @@ function showProfileBaseUrlDialog(
|
|
|
388
446
|
return;
|
|
389
447
|
}
|
|
390
448
|
|
|
391
|
-
|
|
449
|
+
scheduleDialogTransition(() => {
|
|
450
|
+
showProfileApiKeyDialog(api, paths, state, draft);
|
|
451
|
+
});
|
|
392
452
|
},
|
|
393
453
|
onCancel: () => {
|
|
394
|
-
|
|
454
|
+
scheduleDialogTransition(() => {
|
|
455
|
+
showProfileLabelDialog(api, paths, state, action, draft);
|
|
456
|
+
});
|
|
395
457
|
},
|
|
396
458
|
}),
|
|
397
459
|
);
|
|
@@ -403,11 +465,14 @@ function showProfileApiKeyDialog(
|
|
|
403
465
|
state: SetupState,
|
|
404
466
|
draft: ProfileDraft,
|
|
405
467
|
): void {
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
468
|
+
if (!hasShownVisibleApiKeyWarning) {
|
|
469
|
+
hasShownVisibleApiKeyWarning = true;
|
|
470
|
+
showToast(api, {
|
|
471
|
+
variant: "warning",
|
|
472
|
+
message: "API key input is visible while typing.",
|
|
473
|
+
duration: 4000,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
411
476
|
|
|
412
477
|
api.ui.dialog.replace(() =>
|
|
413
478
|
api.ui.DialogPrompt({
|
|
@@ -416,7 +481,7 @@ function showProfileApiKeyDialog(
|
|
|
416
481
|
onConfirm: (value) => {
|
|
417
482
|
const next = value.trim();
|
|
418
483
|
if (next.length === 0) {
|
|
419
|
-
api
|
|
484
|
+
showToast(api, {
|
|
420
485
|
variant: "warning",
|
|
421
486
|
message: "mem9 API key is required.",
|
|
422
487
|
});
|
|
@@ -427,7 +492,9 @@ function showProfileApiKeyDialog(
|
|
|
427
492
|
void submitManualProfile(api, paths, draft);
|
|
428
493
|
},
|
|
429
494
|
onCancel: () => {
|
|
430
|
-
|
|
495
|
+
scheduleDialogTransition(() => {
|
|
496
|
+
showProfileBaseUrlDialog(api, paths, state, "manual-api-key", draft);
|
|
497
|
+
});
|
|
431
498
|
},
|
|
432
499
|
}),
|
|
433
500
|
);
|
|
@@ -459,7 +526,12 @@ function showScopeDialog(
|
|
|
459
526
|
],
|
|
460
527
|
onSelect: (option) => {
|
|
461
528
|
const draft = createScopeDraft(state, option.value);
|
|
462
|
-
|
|
529
|
+
if (mode === "profile-only") {
|
|
530
|
+
showScopeProfileDialog(api, paths, state, draft);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
showScopeDebugDialog(api, paths, state, draft);
|
|
463
535
|
},
|
|
464
536
|
}),
|
|
465
537
|
);
|
|
@@ -469,27 +541,18 @@ function showScopeProfileDialog(
|
|
|
469
541
|
api: TuiPluginApi,
|
|
470
542
|
paths: Mem9ResolvedPaths,
|
|
471
543
|
state: SetupState,
|
|
472
|
-
mode: ScopeFlowMode,
|
|
473
544
|
draft: ScopeDraft,
|
|
474
545
|
): void {
|
|
475
546
|
api.ui.dialog.replace(() =>
|
|
476
547
|
api.ui.DialogSelect<string>({
|
|
477
548
|
title: "Which mem9 profile should this scope use?",
|
|
478
549
|
current: draft.profileId,
|
|
479
|
-
options: state
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
description: `${profile.baseUrl} • ${profile.hasApiKey ? "API key configured" : "API key missing"}`,
|
|
483
|
-
disabled: !profile.hasApiKey,
|
|
484
|
-
})),
|
|
550
|
+
options: buildScopeProfileOptions(state, {
|
|
551
|
+
currentProfileId: draft.profileId,
|
|
552
|
+
}),
|
|
485
553
|
onSelect: (option) => {
|
|
486
554
|
draft.profileId = option.value;
|
|
487
|
-
|
|
488
|
-
void submitScopeConfigDraft(api, paths, draft);
|
|
489
|
-
return;
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
showScopeDebugDialog(api, paths, state, draft);
|
|
555
|
+
void submitScopeConfigDraft(api, paths, draft);
|
|
493
556
|
},
|
|
494
557
|
}),
|
|
495
558
|
);
|
|
@@ -539,7 +602,7 @@ function showDefaultTimeoutDialog(
|
|
|
539
602
|
onConfirm: (value) => {
|
|
540
603
|
const parsed = parsePositiveInteger(value, "defaultTimeoutMs");
|
|
541
604
|
if (parsed === null) {
|
|
542
|
-
api
|
|
605
|
+
showToast(api, {
|
|
543
606
|
variant: "warning",
|
|
544
607
|
message: "Default timeout must be a positive integer.",
|
|
545
608
|
});
|
|
@@ -547,10 +610,12 @@ function showDefaultTimeoutDialog(
|
|
|
547
610
|
}
|
|
548
611
|
|
|
549
612
|
draft.defaultTimeoutMs = parsed;
|
|
550
|
-
|
|
613
|
+
scheduleDialogTransition(() => {
|
|
614
|
+
showSearchTimeoutDialog(api, paths, state, draft);
|
|
615
|
+
});
|
|
551
616
|
},
|
|
552
617
|
onCancel: () => {
|
|
553
|
-
|
|
618
|
+
showScopeDebugDialog(api, paths, state, draft);
|
|
554
619
|
},
|
|
555
620
|
}),
|
|
556
621
|
);
|
|
@@ -570,7 +635,7 @@ function showSearchTimeoutDialog(
|
|
|
570
635
|
onConfirm: (value) => {
|
|
571
636
|
const parsed = parsePositiveInteger(value, "searchTimeoutMs");
|
|
572
637
|
if (parsed === null) {
|
|
573
|
-
api
|
|
638
|
+
showToast(api, {
|
|
574
639
|
variant: "warning",
|
|
575
640
|
message: "Search timeout must be a positive integer.",
|
|
576
641
|
});
|
|
@@ -581,7 +646,9 @@ function showSearchTimeoutDialog(
|
|
|
581
646
|
void submitScopeConfigDraft(api, paths, draft);
|
|
582
647
|
},
|
|
583
648
|
onCancel: () => {
|
|
584
|
-
|
|
649
|
+
scheduleDialogTransition(() => {
|
|
650
|
+
showDefaultTimeoutDialog(api, paths, state, draft);
|
|
651
|
+
});
|
|
585
652
|
},
|
|
586
653
|
}),
|
|
587
654
|
);
|