@askalf/dario 3.32.2 → 3.33.0
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/dist/cc-template.js +15 -0
- package/dist/oauth.d.ts +9 -0
- package/dist/oauth.js +43 -10
- package/package.json +1 -1
package/dist/cc-template.js
CHANGED
|
@@ -255,6 +255,21 @@ export function detectTextToolClient(systemText) {
|
|
|
255
255
|
// preserve-tools is the only correct routing.
|
|
256
256
|
if (/\bYou are Arnie\b/.test(systemText))
|
|
257
257
|
return 'arnie';
|
|
258
|
+
// hands (askalf) — cross-platform computer-use agent built on the
|
|
259
|
+
// Anthropic SDK with computer-use beta tools (computer_20251124,
|
|
260
|
+
// bash_20250124, text_editor_20250728). Identity line is stable
|
|
261
|
+
// across CLI mode ("You are a computer control agent with FULL
|
|
262
|
+
// access to this <os> machine ...") and SDK mode ("You are a
|
|
263
|
+
// computer control agent on <os> ..."). Tool name `bash` overlaps
|
|
264
|
+
// with TOOL_MAP, but the wire shape is Anthropic's beta computer-
|
|
265
|
+
// use tool (`type: 'bash_20250124'`, no `command`/`description`
|
|
266
|
+
// schema) — default round-robin remap would corrupt those calls
|
|
267
|
+
// and lose the `computer` / `text_editor` tools entirely (neither
|
|
268
|
+
// is in TOOL_MAP, structural fallback won't catch them at the
|
|
269
|
+
// 80% threshold either). Identity match → auto preserve-tools,
|
|
270
|
+
// like arnie.
|
|
271
|
+
if (/\bYou are a computer control agent\b/.test(systemText))
|
|
272
|
+
return 'hands';
|
|
258
273
|
// Protocol-signature fallback — unique to the Cline family and its
|
|
259
274
|
// forks; survives a forked system prompt that edited the identity
|
|
260
275
|
// string out but kept the tool protocol intact.
|
package/dist/oauth.d.ts
CHANGED
|
@@ -14,6 +14,15 @@ export interface CredentialsFile {
|
|
|
14
14
|
claudeAiOauth: OAuthTokens;
|
|
15
15
|
}
|
|
16
16
|
export declare function loadCredentials(): Promise<CredentialsFile | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Pick the freshest of a set of `CredentialsFile` candidates by
|
|
19
|
+
* `expiresAt` (unix-ms timestamp; missing/zero sorts last). Stable on
|
|
20
|
+
* ties — the first-pushed candidate wins when expiresAt is equal,
|
|
21
|
+
* which means the canonical call order
|
|
22
|
+
* `[darioFile, ccFile, keychain]` keeps the dario-file source as the
|
|
23
|
+
* tiebreaker preference. Exported for direct testing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pickFreshestCredentials(candidates: CredentialsFile[]): CredentialsFile | null;
|
|
17
26
|
/**
|
|
18
27
|
* Automatic OAuth flow using a local callback server (same as Claude Code).
|
|
19
28
|
* Opens browser, captures the authorization code automatically.
|
package/dist/oauth.js
CHANGED
|
@@ -159,27 +159,60 @@ export async function loadCredentials() {
|
|
|
159
159
|
if (credentialsCache && Date.now() - credentialsCacheTime < CACHE_TTL_MS) {
|
|
160
160
|
return credentialsCache;
|
|
161
161
|
}
|
|
162
|
-
//
|
|
162
|
+
// Read every available source (dario file, CC file, OS keychain) and
|
|
163
|
+
// pick the freshest. Previously this returned the first source that
|
|
164
|
+
// had both tokens regardless of expiry — which means a stale
|
|
165
|
+
// ~/.dario/credentials.json (left over from a prior `dario login`
|
|
166
|
+
// whose refresh_token has since been invalidated by Anthropic) would
|
|
167
|
+
// shadow CC's still-fresh ~/.claude/.credentials.json forever, with
|
|
168
|
+
// no automatic recovery. Picking the freshest makes auto-detection
|
|
169
|
+
// work the way it did before any `dario login` had ever run, while
|
|
170
|
+
// still preferring dario's own file when both sources are equivalent
|
|
171
|
+
// (dario file wins ties on expiresAt by being checked first).
|
|
172
|
+
const candidates = [];
|
|
163
173
|
for (const path of [getDarioCredentialsPath(), getClaudeCodeCredentialsPath()]) {
|
|
164
174
|
try {
|
|
165
175
|
const raw = await readFile(path, 'utf-8');
|
|
166
176
|
const parsed = JSON.parse(raw);
|
|
167
177
|
if (parsed?.claudeAiOauth?.accessToken && parsed?.claudeAiOauth?.refreshToken) {
|
|
168
|
-
|
|
169
|
-
credentialsCacheTime = Date.now();
|
|
170
|
-
return credentialsCache;
|
|
178
|
+
candidates.push(parsed);
|
|
171
179
|
}
|
|
172
180
|
}
|
|
173
181
|
catch { /* try next */ }
|
|
174
182
|
}
|
|
175
|
-
//
|
|
183
|
+
// OS keychain (modern CC stores credentials here, not on disk).
|
|
176
184
|
const keychainCreds = await loadKeychainCredentials();
|
|
177
|
-
if (keychainCreds) {
|
|
178
|
-
|
|
179
|
-
credentialsCacheTime = Date.now();
|
|
180
|
-
return credentialsCache;
|
|
185
|
+
if (keychainCreds?.claudeAiOauth?.accessToken && keychainCreds?.claudeAiOauth?.refreshToken) {
|
|
186
|
+
candidates.push(keychainCreds);
|
|
181
187
|
}
|
|
182
|
-
|
|
188
|
+
const best = pickFreshestCredentials(candidates);
|
|
189
|
+
if (!best)
|
|
190
|
+
return null;
|
|
191
|
+
credentialsCache = best;
|
|
192
|
+
credentialsCacheTime = Date.now();
|
|
193
|
+
return credentialsCache;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Pick the freshest of a set of `CredentialsFile` candidates by
|
|
197
|
+
* `expiresAt` (unix-ms timestamp; missing/zero sorts last). Stable on
|
|
198
|
+
* ties — the first-pushed candidate wins when expiresAt is equal,
|
|
199
|
+
* which means the canonical call order
|
|
200
|
+
* `[darioFile, ccFile, keychain]` keeps the dario-file source as the
|
|
201
|
+
* tiebreaker preference. Exported for direct testing.
|
|
202
|
+
*/
|
|
203
|
+
export function pickFreshestCredentials(candidates) {
|
|
204
|
+
if (candidates.length === 0)
|
|
205
|
+
return null;
|
|
206
|
+
let best = candidates[0];
|
|
207
|
+
let bestExp = best.claudeAiOauth.expiresAt ?? 0;
|
|
208
|
+
for (let i = 1; i < candidates.length; i++) {
|
|
209
|
+
const exp = candidates[i].claudeAiOauth.expiresAt ?? 0;
|
|
210
|
+
if (exp > bestExp) {
|
|
211
|
+
best = candidates[i];
|
|
212
|
+
bestExp = exp;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return best;
|
|
183
216
|
}
|
|
184
217
|
async function saveCredentials(creds) {
|
|
185
218
|
const path = getDarioCredentialsPath();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.33.0",
|
|
4
4
|
"description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|