nemoris 0.1.18 → 0.1.20
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/package.json +4 -2
- package/src/auth/auth-profiles.js +7 -2
- package/src/onboarding/auth/api-key.js +52 -12
- package/src/onboarding/clack-prompter.js +35 -6
- package/src/onboarding/model-catalog.js +14 -14
- package/src/onboarding/phases/auth.js +305 -127
- package/src/onboarding/phases/build.js +61 -35
- package/src/onboarding/setup-checklist.js +30 -9
- package/src/onboarding/templates.js +6 -6
- package/src/onboarding/wizard.js +624 -160
- package/src/shadow/bridge.js +13 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nemoris",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.20",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Personal AI agent runtime — persistent memory, delivery guarantees, task contracts, self-healing. Local-first, no cloud.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -73,7 +73,9 @@
|
|
|
73
73
|
"publish:check": "node scripts/check-publish-dry-run.js",
|
|
74
74
|
"status": "node src/cli.js runtime-status",
|
|
75
75
|
"test": "node --test",
|
|
76
|
-
"test:e2e": "node tests/e2e/run-report.js"
|
|
76
|
+
"test:e2e": "node tests/e2e/run-report.js",
|
|
77
|
+
"test:e2e:interactive": "node --test tests/e2e/onboarding-interactive.test.js",
|
|
78
|
+
"test:e2e:setup-sweep": "node scripts/e2e-setup-sweep.js"
|
|
77
79
|
},
|
|
78
80
|
"engines": {
|
|
79
81
|
"node": ">=22.5.0"
|
|
@@ -20,12 +20,17 @@ export function resolveInstallDir({ env = process.env, cwd: _cwd = process.cwd()
|
|
|
20
20
|
}
|
|
21
21
|
return resolved;
|
|
22
22
|
}
|
|
23
|
-
//
|
|
23
|
+
// Default data dir — prefer this if it exists with actual config
|
|
24
|
+
const defaultDataDir = path.join(os.homedir(), ".nemoris-data");
|
|
25
|
+
if (fs.existsSync(path.join(defaultDataDir, "config"))) {
|
|
26
|
+
return defaultDataDir;
|
|
27
|
+
}
|
|
28
|
+
// Dev mode: when CWD is the source repo and no ~/.nemoris-data config exists
|
|
24
29
|
if (_cwd && fs.existsSync(path.join(_cwd, "package.json")) &&
|
|
25
30
|
fs.existsSync(path.join(_cwd, "src", "cli.js"))) {
|
|
26
31
|
return _cwd;
|
|
27
32
|
}
|
|
28
|
-
return
|
|
33
|
+
return defaultDataDir;
|
|
29
34
|
}
|
|
30
35
|
|
|
31
36
|
export function resolveAuthProfilesPath({ env = process.env, cwd = process.cwd() } = {}) {
|
|
@@ -143,19 +143,59 @@ export async function validateApiKey(provider, key, options = {}) {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
const fetch = options.fetchImpl || globalThis.fetch;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
146
|
+
if (provider === "anthropic") {
|
|
147
|
+
const probes = [
|
|
148
|
+
{
|
|
149
|
+
url: "https://api.anthropic.com/v1/models",
|
|
150
|
+
method: "GET",
|
|
151
|
+
headers: {
|
|
152
|
+
"x-api-key": key,
|
|
153
|
+
"anthropic-version": "2023-06-01"
|
|
154
|
+
}
|
|
153
155
|
},
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
{
|
|
157
|
+
url: "https://api.anthropic.com/v1/models",
|
|
158
|
+
method: "GET",
|
|
159
|
+
headers: {
|
|
160
|
+
Authorization: `Bearer ${key}`,
|
|
161
|
+
"anthropic-version": "2023-06-01"
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
url: "https://api.anthropic.com/v1/messages/count_tokens",
|
|
166
|
+
method: "POST",
|
|
167
|
+
headers: {
|
|
168
|
+
"content-type": "application/json",
|
|
169
|
+
...buildAnthropicAuthHeaders(key)
|
|
170
|
+
},
|
|
171
|
+
body: JSON.stringify({
|
|
172
|
+
model: "claude-haiku-4-5",
|
|
173
|
+
messages: [{ role: "user", content: "ping" }]
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
];
|
|
177
|
+
|
|
178
|
+
let lastFailure = { ok: false, error: "request failed" };
|
|
179
|
+
for (const probe of probes) {
|
|
180
|
+
try {
|
|
181
|
+
const response = await fetch(probe.url, {
|
|
182
|
+
method: probe.method,
|
|
183
|
+
headers: probe.headers,
|
|
184
|
+
body: probe.body,
|
|
185
|
+
signal: AbortSignal.timeout(10000)
|
|
186
|
+
});
|
|
187
|
+
if (response.ok) {
|
|
188
|
+
return { ok: true, status: response.status };
|
|
189
|
+
}
|
|
190
|
+
lastFailure = { ok: false, status: response.status };
|
|
191
|
+
} catch (error) {
|
|
192
|
+
lastFailure = { ok: false, error: error.message };
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return lastFailure;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const providerTargets = {
|
|
159
199
|
openai: {
|
|
160
200
|
url: "https://api.openai.com/v1/models",
|
|
161
201
|
method: "GET",
|
|
@@ -23,6 +23,27 @@ function normalizeOptions(options = []) {
|
|
|
23
23
|
}));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function normalizeSearchTokens(search = "") {
|
|
27
|
+
return String(search)
|
|
28
|
+
.toLowerCase()
|
|
29
|
+
.split(/\s+/)
|
|
30
|
+
.map((token) => token.trim())
|
|
31
|
+
.filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function buildOptionSearchText(option = {}) {
|
|
35
|
+
return `${option.label || ""} ${option.hint || ""} ${option.value || ""}`.toLowerCase();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function tokenizedOptionFilter(search, option) {
|
|
39
|
+
const tokens = normalizeSearchTokens(search);
|
|
40
|
+
if (tokens.length === 0) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
const haystack = buildOptionSearchText(option);
|
|
44
|
+
return tokens.every((token) => haystack.includes(token));
|
|
45
|
+
}
|
|
46
|
+
|
|
26
47
|
export function createClackPrompter() {
|
|
27
48
|
return {
|
|
28
49
|
intro(message) {
|
|
@@ -48,12 +69,20 @@ export function createClackPrompter() {
|
|
|
48
69
|
}));
|
|
49
70
|
},
|
|
50
71
|
async multiselect({ message, options, initialValues = [], required = false }) {
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
const normalized = normalizeOptions(options);
|
|
73
|
+
const value = guardCancel(await (required || !p.autocompleteMultiselect
|
|
74
|
+
? p.multiselect({
|
|
75
|
+
message,
|
|
76
|
+
options: normalized,
|
|
77
|
+
initialValues,
|
|
78
|
+
required,
|
|
79
|
+
})
|
|
80
|
+
: p.autocompleteMultiselect({
|
|
81
|
+
message,
|
|
82
|
+
options: normalized,
|
|
83
|
+
initialValues,
|
|
84
|
+
filter: tokenizedOptionFilter,
|
|
85
|
+
})));
|
|
57
86
|
return Array.isArray(value) ? value : [];
|
|
58
87
|
},
|
|
59
88
|
async text({ message, placeholder, initialValue, validate }) {
|
|
@@ -10,18 +10,18 @@ export const PROVIDER_ENV_KEYS = {
|
|
|
10
10
|
export const CURATED_MODELS = {
|
|
11
11
|
anthropic: [
|
|
12
12
|
{
|
|
13
|
-
value: "anthropic/claude-
|
|
14
|
-
label: "claude-
|
|
15
|
-
hint: "ctx 200k ·
|
|
13
|
+
value: "anthropic/claude-sonnet-4.6",
|
|
14
|
+
label: "claude-sonnet-4.6",
|
|
15
|
+
hint: "ctx 200k · balanced · recommended",
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
|
-
value: "anthropic/claude-
|
|
19
|
-
label: "claude-
|
|
20
|
-
hint: "ctx 200k ·
|
|
18
|
+
value: "anthropic/claude-haiku-4.5",
|
|
19
|
+
label: "claude-haiku-4.5",
|
|
20
|
+
hint: "ctx 200k · fast · cheapest",
|
|
21
21
|
},
|
|
22
22
|
{
|
|
23
|
-
value: "anthropic/claude-opus-4
|
|
24
|
-
label: "claude-opus-4
|
|
23
|
+
value: "anthropic/claude-opus-4.6",
|
|
24
|
+
label: "claude-opus-4.6",
|
|
25
25
|
hint: "ctx 200k · most capable · expensive",
|
|
26
26
|
},
|
|
27
27
|
],
|
|
@@ -50,13 +50,13 @@ export const CURATED_MODELS = {
|
|
|
50
50
|
openrouter: [
|
|
51
51
|
// ── Top picks (most popular on OpenRouter) ──
|
|
52
52
|
{
|
|
53
|
-
value: "openrouter/anthropic/claude-sonnet-4
|
|
54
|
-
label: "anthropic/claude-sonnet-4
|
|
53
|
+
value: "openrouter/anthropic/claude-sonnet-4.6",
|
|
54
|
+
label: "anthropic/claude-sonnet-4.6",
|
|
55
55
|
hint: "ctx 200k · balanced · recommended",
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
|
-
value: "openrouter/anthropic/claude-haiku-4
|
|
59
|
-
label: "anthropic/claude-haiku-4
|
|
58
|
+
value: "openrouter/anthropic/claude-haiku-4.5",
|
|
59
|
+
label: "anthropic/claude-haiku-4.5",
|
|
60
60
|
hint: "ctx 200k · fast · cheap",
|
|
61
61
|
},
|
|
62
62
|
{
|
|
@@ -70,8 +70,8 @@ export const CURATED_MODELS = {
|
|
|
70
70
|
hint: "ctx 164k · strong · very cheap",
|
|
71
71
|
},
|
|
72
72
|
{
|
|
73
|
-
value: "openrouter/anthropic/claude-opus-4
|
|
74
|
-
label: "anthropic/claude-opus-4
|
|
73
|
+
value: "openrouter/anthropic/claude-opus-4.6",
|
|
74
|
+
label: "anthropic/claude-opus-4.6",
|
|
75
75
|
hint: "ctx 200k · most capable · expensive",
|
|
76
76
|
},
|
|
77
77
|
// ── More options ──
|