mixdog 0.7.5 → 0.7.7
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-plugin/plugin.json +1 -1
- package/CHANGELOG.md +18 -0
- package/README.md +18 -0
- package/hooks/hooks.json +6 -6
- package/hooks/session-start.cjs +73 -2
- package/hooks/shim-launcher.cjs +51 -0
- package/native/prebuilt/linux-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/linux-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/macos-aarch64/mixdog-shim +0 -0
- package/native/prebuilt/macos-x86_64/mixdog-shim +0 -0
- package/native/prebuilt/windows-x86_64/mixdog-shim.exe +0 -0
- package/package.json +2 -2
- package/scripts/bootstrap.mjs +5 -59
- package/scripts/ensure-deps.mjs +259 -0
- package/scripts/resolve-bun.mjs +60 -0
- package/scripts/run-mcp.mjs +13 -168
- package/setup/install.mjs +220 -22
- package/setup/launch.mjs +0 -0
- package/setup/locate-claude.mjs +38 -0
- package/setup/mixdog-cli.mjs +95 -0
- package/setup/setup-server.mjs +50 -2
- package/setup/setup.html +26 -12
- package/setup/tui.mjs +606 -0
- package/setup/wizard.mjs +220 -151
- package/src/agent/bridge-stall-watchdog.mjs +2 -2
- package/src/agent/index.mjs +3 -3
- package/src/agent/orchestrator/providers/anthropic-oauth.mjs +139 -0
- package/src/agent/orchestrator/providers/openai-oauth.mjs +96 -0
- package/src/agent/orchestrator/session/manager.mjs +5 -3
- package/src/agent/orchestrator/session/store.mjs +9 -1
- package/src/channels/lib/runtime-paths.mjs +112 -74
- package/src/memory/index.mjs +30 -7
- package/src/memory/lib/pg/supervisor.mjs +12 -12
- package/src/shared/atomic-file.mjs +16 -0
- package/src/status/aggregator.mjs +3 -3
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// mixdog-cli.mjs — `mixdog` bin dispatcher: launch Claude Code with the dev
|
|
3
|
+
// plugin load flags for other args; no args or `install` → runInstall().
|
|
4
|
+
|
|
5
|
+
import { spawn } from 'node:child_process';
|
|
6
|
+
import { realpathSync } from 'node:fs';
|
|
7
|
+
import { constants as osConstants } from 'node:os';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
import { DEFAULT_MARKETPLACE, DEFAULT_PLUGIN } from '../src/shared/plugin-paths.mjs';
|
|
10
|
+
import { runInstall } from './install.mjs';
|
|
11
|
+
import { resolveClaudeExecutable } from './locate-claude.mjs';
|
|
12
|
+
|
|
13
|
+
export { resolveClaudeExecutable } from './locate-claude.mjs';
|
|
14
|
+
|
|
15
|
+
const PLUGIN_LOAD_ARG = `plugin:${DEFAULT_PLUGIN}@${DEFAULT_MARKETPLACE}`;
|
|
16
|
+
const CLAUDE_PREFIX = ['--dangerously-load-development-channels', PLUGIN_LOAD_ARG];
|
|
17
|
+
|
|
18
|
+
export function buildClaudeLaunchArgv(passthrough = []) {
|
|
19
|
+
return [...CLAUDE_PREFIX, ...passthrough];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function dispatchMixdogCli(argv = process.argv.slice(2)) {
|
|
23
|
+
const [first] = argv;
|
|
24
|
+
if (argv.length === 0 || first === 'install') {
|
|
25
|
+
if (process.env.MIXDOG_CLI_DRY_RUN === '1') {
|
|
26
|
+
process.stdout.write('mixdog-cli: route=setup\n');
|
|
27
|
+
return 0;
|
|
28
|
+
}
|
|
29
|
+
await runInstall();
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const claudeArgs = buildClaudeLaunchArgv(argv);
|
|
34
|
+
if (process.env.MIXDOG_CLI_DRY_RUN === '1') {
|
|
35
|
+
process.stdout.write(`mixdog-cli: claude ${JSON.stringify(claudeArgs)}\n`);
|
|
36
|
+
return 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const claudePath = resolveClaudeExecutable();
|
|
40
|
+
if (!claudePath) {
|
|
41
|
+
process.stderr.write(
|
|
42
|
+
'\n✗ `claude` was not found on PATH. Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code\n',
|
|
43
|
+
);
|
|
44
|
+
return 127;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return launchClaude(claudePath, claudeArgs);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function launchClaude(claudePath, claudeArgs) {
|
|
51
|
+
return new Promise((resolve) => {
|
|
52
|
+
const win32 = process.platform === 'win32';
|
|
53
|
+
const needsShell = win32 && /\.(cmd|bat)$/i.test(claudePath);
|
|
54
|
+
const child = spawn(claudePath, claudeArgs, {
|
|
55
|
+
stdio: 'inherit',
|
|
56
|
+
shell: needsShell,
|
|
57
|
+
windowsHide: true,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
child.on('error', (err) => {
|
|
61
|
+
process.stderr.write(`${err?.stack || err?.message || String(err)}\n`);
|
|
62
|
+
resolve(1);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
child.on('close', (code, signal) => {
|
|
66
|
+
if (signal) {
|
|
67
|
+
const sigNum = osConstants.signals?.[signal] ?? 0;
|
|
68
|
+
resolve(128 + sigNum);
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
resolve(code ?? 0);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function isDirectCliEntry() {
|
|
77
|
+
const entry = process.argv[1];
|
|
78
|
+
if (!entry) return false;
|
|
79
|
+
try {
|
|
80
|
+
const self = realpathSync(fileURLToPath(import.meta.url));
|
|
81
|
+
const invoked = realpathSync(entry);
|
|
82
|
+
return self === invoked;
|
|
83
|
+
} catch {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (isDirectCliEntry()) {
|
|
89
|
+
dispatchMixdogCli()
|
|
90
|
+
.then((code) => process.exit(code))
|
|
91
|
+
.catch((err) => {
|
|
92
|
+
console.error(err?.stack || err?.message || String(err));
|
|
93
|
+
process.exit(1);
|
|
94
|
+
});
|
|
95
|
+
}
|
package/setup/setup-server.mjs
CHANGED
|
@@ -7,8 +7,8 @@ import { fileURLToPath } from 'url';
|
|
|
7
7
|
import http from 'http';
|
|
8
8
|
import https from 'https';
|
|
9
9
|
import { DEFAULT_MAINTENANCE, MAINTENANCE_SLOTS, DEFAULT_PRESETS, getPluginData } from '../src/agent/orchestrator/config.mjs';
|
|
10
|
-
import { getOpenAIOAuthModelCatalogError, hasOpenAIOAuthCredentials } from '../src/agent/orchestrator/providers/openai-oauth.mjs';
|
|
11
|
-
import { hasAnthropicOAuthCredentials } from '../src/agent/orchestrator/providers/anthropic-oauth.mjs';
|
|
10
|
+
import { getOpenAIOAuthModelCatalogError, hasOpenAIOAuthCredentials, loginOAuth as loginOpenAIOAuth } from '../src/agent/orchestrator/providers/openai-oauth.mjs';
|
|
11
|
+
import { hasAnthropicOAuthCredentials, loginOAuth as loginAnthropicOAuth } from '../src/agent/orchestrator/providers/anthropic-oauth.mjs';
|
|
12
12
|
import { hasGrokOAuthCredentials, loginOAuth as loginGrokOAuth } from '../src/agent/orchestrator/providers/grok-oauth.mjs';
|
|
13
13
|
import { resolvePluginData } from '../src/shared/plugin-paths.mjs';
|
|
14
14
|
import { listSchedules } from '../src/shared/schedules-store.mjs';
|
|
@@ -2135,6 +2135,54 @@ async function handleRequest(req, res) {
|
|
|
2135
2135
|
return;
|
|
2136
2136
|
}
|
|
2137
2137
|
|
|
2138
|
+
if (req.method === 'POST' && path === '/agent/openai-oauth/login') {
|
|
2139
|
+
if (!isAllowedOrigin(req)) {
|
|
2140
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
2141
|
+
res.end(JSON.stringify({ ok: false, error: 'forbidden: cross-origin' }));
|
|
2142
|
+
return;
|
|
2143
|
+
}
|
|
2144
|
+
try {
|
|
2145
|
+
const tokens = await loginOpenAIOAuth();
|
|
2146
|
+
if (!tokens?.access_token) {
|
|
2147
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2148
|
+
res.end(JSON.stringify({ ok: false, error: 'login cancelled or timed out' }));
|
|
2149
|
+
return;
|
|
2150
|
+
}
|
|
2151
|
+
dropRuntimeModelCaches();
|
|
2152
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2153
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2154
|
+
} catch (e) {
|
|
2155
|
+
process.stderr.write('[setup] /agent/openai-oauth/login failed: ' + (e?.stack || e?.message || String(e)) + '\n');
|
|
2156
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
2157
|
+
res.end(JSON.stringify({ ok: false, error: e?.message || String(e) }));
|
|
2158
|
+
}
|
|
2159
|
+
return;
|
|
2160
|
+
}
|
|
2161
|
+
|
|
2162
|
+
if (req.method === 'POST' && path === '/agent/anthropic-oauth/login') {
|
|
2163
|
+
if (!isAllowedOrigin(req)) {
|
|
2164
|
+
res.writeHead(403, { 'Content-Type': 'application/json' });
|
|
2165
|
+
res.end(JSON.stringify({ ok: false, error: 'forbidden: cross-origin' }));
|
|
2166
|
+
return;
|
|
2167
|
+
}
|
|
2168
|
+
try {
|
|
2169
|
+
const tokens = await loginAnthropicOAuth();
|
|
2170
|
+
if (!tokens?.accessToken) {
|
|
2171
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2172
|
+
res.end(JSON.stringify({ ok: false, error: 'login cancelled or timed out' }));
|
|
2173
|
+
return;
|
|
2174
|
+
}
|
|
2175
|
+
dropRuntimeModelCaches();
|
|
2176
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2177
|
+
res.end(JSON.stringify({ ok: true }));
|
|
2178
|
+
} catch (e) {
|
|
2179
|
+
process.stderr.write('[setup] /agent/anthropic-oauth/login failed: ' + (e?.stack || e?.message || String(e)) + '\n');
|
|
2180
|
+
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
2181
|
+
res.end(JSON.stringify({ ok: false, error: e?.message || String(e) }));
|
|
2182
|
+
}
|
|
2183
|
+
return;
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2138
2186
|
if (req.method === 'GET' && path === '/agent/presets') {
|
|
2139
2187
|
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
2140
2188
|
res.end(JSON.stringify({ presets: readAgentPresets() }));
|
package/setup/setup.html
CHANGED
|
@@ -2607,8 +2607,8 @@ const AG_API_PROVIDERS = [
|
|
|
2607
2607
|
{id:'nvidia',name:'NVIDIA',env:'NVIDIA_API_KEY',url:'https://build.nvidia.com'},
|
|
2608
2608
|
];
|
|
2609
2609
|
const AG_OAUTH_PROVIDERS = [
|
|
2610
|
-
{id:'openai-oauth',name:'Codex',desc:'~/.codex/auth.json'},
|
|
2611
|
-
{id:'anthropic-oauth',name:'Claude Code',desc:'~/.claude/.credentials.json'},
|
|
2610
|
+
{id:'openai-oauth',name:'Codex',desc:'~/.codex/auth.json',login:true},
|
|
2611
|
+
{id:'anthropic-oauth',name:'Claude Code',desc:'~/.claude/.credentials.json',login:true},
|
|
2612
2612
|
{id:'grok-oauth',name:'Grok',desc:'~/.grok/auth.json — or browser OAuth (consent: "Grok Build")',login:true},
|
|
2613
2613
|
];
|
|
2614
2614
|
const AG_LOCAL_PROVIDERS = [
|
|
@@ -2673,8 +2673,9 @@ function agRenderAll() {
|
|
|
2673
2673
|
for (const p of AG_OAUTH_PROVIDERS) {
|
|
2674
2674
|
const detected = agOAuthDetected(p.id);
|
|
2675
2675
|
const tag = detected ? '<span class="r-tag ok">Set</span>' : '<span class="r-tag no">Not Set</span>';
|
|
2676
|
-
|
|
2677
|
-
|
|
2676
|
+
const btn = p.login
|
|
2677
|
+
? `<button class="save" style="width:auto;height:28px;padding:0 12px;font-size:11px;flex-shrink:0;margin-left:8px" type="button" onclick="oauthProviderLogin('${escapeAttr(p.id)}', this)">${detected ? 'Re-login' : 'Sign in'}</button>`
|
|
2678
|
+
: '';
|
|
2678
2679
|
oauthSec.innerHTML += `<div class="r"><span class="r-name">${p.name}</span><span style="flex:1;font-size:12px;color:var(--text-4)">${p.desc}</span>${tag}${btn}</div>`;
|
|
2679
2680
|
}
|
|
2680
2681
|
const localSec = document.getElementById('ag-local-sec');
|
|
@@ -2733,18 +2734,31 @@ function agOAuthDetected(providerId, auth = agAuth) {
|
|
|
2733
2734
|
return false;
|
|
2734
2735
|
}
|
|
2735
2736
|
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2737
|
+
const OAUTH_LOGIN_ROUTES = {
|
|
2738
|
+
'openai-oauth': '/agent/openai-oauth/login',
|
|
2739
|
+
'anthropic-oauth': '/agent/anthropic-oauth/login',
|
|
2740
|
+
'grok-oauth': '/agent/grok-oauth/login',
|
|
2741
|
+
};
|
|
2742
|
+
const OAUTH_LOGIN_LABELS = {
|
|
2743
|
+
'openai-oauth': 'Codex',
|
|
2744
|
+
'anthropic-oauth': 'Claude',
|
|
2745
|
+
'grok-oauth': 'Grok',
|
|
2746
|
+
};
|
|
2747
|
+
|
|
2748
|
+
// Browser OAuth login for providers with login:true. CLI cred files (~/.codex,
|
|
2749
|
+
// ~/.claude, ~/.grok) show as "Set" automatically; Sign in runs when none exist.
|
|
2750
|
+
// Blocks until consent completes, then re-fetches auth so the row flips to Set.
|
|
2751
|
+
async function oauthProviderLogin(providerId, btn) {
|
|
2752
|
+
const route = OAUTH_LOGIN_ROUTES[providerId];
|
|
2753
|
+
if (!route) return;
|
|
2754
|
+
const label = OAUTH_LOGIN_LABELS[providerId] || providerId;
|
|
2741
2755
|
if (btn) { btn.disabled = true; btn.textContent = 'Waiting for browser…'; }
|
|
2742
2756
|
try {
|
|
2743
|
-
const r = await fetch(
|
|
2757
|
+
const r = await fetch(route, { method: 'POST' }).then(r => r.json());
|
|
2744
2758
|
if (r && r.ok) { await loadAgentData(); }
|
|
2745
|
-
else { alert('
|
|
2759
|
+
else { alert(label + ' login failed: ' + ((r && r.error) || 'unknown error')); if (btn) btn.disabled = false; }
|
|
2746
2760
|
} catch (e) {
|
|
2747
|
-
alert('
|
|
2761
|
+
alert(label + ' login failed: ' + (e?.message || e));
|
|
2748
2762
|
if (btn) btn.disabled = false;
|
|
2749
2763
|
}
|
|
2750
2764
|
}
|