@adhdev/daemon-core 0.8.57 → 0.8.58
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/boot/daemon-lifecycle.d.ts +1 -0
- package/dist/commands/handler.d.ts +1 -0
- package/dist/commands/stream-commands.d.ts +2 -0
- package/dist/config/chat-history.d.ts +3 -0
- package/dist/config/config.d.ts +3 -0
- package/dist/config/provider-source-config.d.ts +23 -0
- package/dist/daemon/dev-server-types.d.ts +1 -0
- package/dist/daemon/dev-server.d.ts +4 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +316 -155
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +315 -155
- package/dist/index.mjs.map +1 -1
- package/dist/providers/cli-provider-instance.d.ts +9 -0
- package/dist/providers/contracts.d.ts +1 -0
- package/dist/providers/provider-loader.d.ts +14 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/boot/daemon-lifecycle.ts +13 -3
- package/src/commands/handler.ts +3 -0
- package/src/commands/router.ts +5 -4
- package/src/commands/stream-commands.ts +41 -0
- package/src/config/chat-history.ts +39 -0
- package/src/config/config.d.ts +3 -0
- package/src/config/config.ts +19 -3
- package/src/config/provider-source-config.ts +42 -0
- package/src/daemon/dev-auto-implement.ts +0 -13
- package/src/daemon/dev-server-types.ts +1 -0
- package/src/daemon/dev-server.ts +45 -20
- package/src/index.ts +2 -0
- package/src/providers/cli-provider-instance.ts +25 -0
- package/src/providers/contracts.ts +1 -0
- package/src/providers/provider-loader.d.ts +4 -1
- package/src/providers/provider-loader.ts +61 -23
- package/src/providers/provider-schema.ts +3 -0
|
@@ -9,6 +9,15 @@ import type { ProviderInstance, ProviderState, InstanceContext } from './provide
|
|
|
9
9
|
import { ProviderCliAdapter } from '../cli-adapters/provider-cli-adapter.js';
|
|
10
10
|
import type { PtyTransportFactory } from '../cli-adapters/pty-transport.js';
|
|
11
11
|
export declare function getForcedNewSessionScriptName(provider: ProviderModule | undefined, launchMode: 'new' | 'resume' | 'manual'): string | null;
|
|
12
|
+
export declare function waitForCliAdapterReady(adapter: {
|
|
13
|
+
isReady?: () => boolean;
|
|
14
|
+
getStatus?: () => {
|
|
15
|
+
status?: string;
|
|
16
|
+
};
|
|
17
|
+
}, options?: {
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
pollMs?: number;
|
|
20
|
+
}): Promise<void>;
|
|
12
21
|
export declare class CliProviderInstance implements ProviderInstance {
|
|
13
22
|
private provider;
|
|
14
23
|
private workingDir;
|
|
@@ -328,6 +328,7 @@ export interface ProviderModule {
|
|
|
328
328
|
/** How the CLI adapter decides when to submit typed input */
|
|
329
329
|
submitStrategy?: 'wait_for_echo' | 'immediate';
|
|
330
330
|
/** Keep this provider out of the upstream auto-updated bundle */
|
|
331
|
+
/** @deprecated Machine-level provider source policy now lives in config.providerSourceMode. Local overrides shadow upstream by root precedence and should not rely on provider-level disableUpstream. */
|
|
331
332
|
disableUpstream?: boolean;
|
|
332
333
|
approvalKeys?: Record<number, string>;
|
|
333
334
|
patterns?: {
|
|
@@ -14,11 +14,16 @@
|
|
|
14
14
|
*/
|
|
15
15
|
import { VersionArchive } from './version-archive.js';
|
|
16
16
|
import type { ProviderModule, ProviderCategory, ProviderSettingSchema, ResolvedProvider } from './contracts.js';
|
|
17
|
+
import type { ProviderSourceMode } from '../config/config.js';
|
|
18
|
+
import type { ProviderSourceConfigSnapshot } from '../config/provider-source-config.js';
|
|
17
19
|
export declare class ProviderLoader {
|
|
18
20
|
private providers;
|
|
19
21
|
private providerAvailability;
|
|
22
|
+
private defaultProvidersDir;
|
|
23
|
+
private explicitProviderDir;
|
|
20
24
|
private userDir;
|
|
21
25
|
private upstreamDir;
|
|
26
|
+
private sourceMode;
|
|
22
27
|
private disableUpstream;
|
|
23
28
|
private watchers;
|
|
24
29
|
private logFn;
|
|
@@ -31,7 +36,9 @@ export declare class ProviderLoader {
|
|
|
31
36
|
constructor(options?: {
|
|
32
37
|
userDir?: string;
|
|
33
38
|
logFn?: (msg: string) => void;
|
|
34
|
-
/**
|
|
39
|
+
/** Explicit machine-level provider source policy */
|
|
40
|
+
sourceMode?: ProviderSourceMode;
|
|
41
|
+
/** Deprecated alias for sourceMode='no-upstream' */
|
|
35
42
|
disableUpstream?: boolean;
|
|
36
43
|
});
|
|
37
44
|
private log;
|
|
@@ -48,6 +55,12 @@ export declare class ProviderLoader {
|
|
|
48
55
|
* Highest-priority editable overrides come first.
|
|
49
56
|
*/
|
|
50
57
|
getProviderRoots(): string[];
|
|
58
|
+
getSourceConfig(): ProviderSourceConfigSnapshot;
|
|
59
|
+
applySourceConfig(options?: {
|
|
60
|
+
userDir?: string;
|
|
61
|
+
sourceMode?: ProviderSourceMode;
|
|
62
|
+
disableUpstream?: boolean;
|
|
63
|
+
}): ProviderSourceConfigSnapshot;
|
|
51
64
|
/**
|
|
52
65
|
* Canonical provider directory shape for a given root.
|
|
53
66
|
*/
|
package/package.json
CHANGED
|
@@ -90,6 +90,7 @@ export interface DaemonComponents {
|
|
|
90
90
|
cdpManagers: Map<string, DaemonCdpManager>;
|
|
91
91
|
sessionRegistry: SessionRegistry;
|
|
92
92
|
detectedIdes: { value: IDEInfo[] };
|
|
93
|
+
refreshProviderAvailability: (providerType?: string) => Promise<void>;
|
|
93
94
|
}
|
|
94
95
|
|
|
95
96
|
export interface DaemonDevSupportOptions {
|
|
@@ -116,12 +117,13 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
116
117
|
// 1. Global log interceptor
|
|
117
118
|
installGlobalInterceptor();
|
|
118
119
|
|
|
119
|
-
// 2. ProviderLoader (
|
|
120
|
+
// 2. ProviderLoader (provider source mode from config.json)
|
|
120
121
|
const appConfig = loadConfig();
|
|
121
|
-
const
|
|
122
|
+
const providerSourceMode = appConfig.providerSourceMode || 'normal';
|
|
123
|
+
const disableUpstream = providerSourceMode === 'no-upstream';
|
|
122
124
|
const providerLoader = new ProviderLoader({
|
|
123
125
|
logFn: config.providerLogFn,
|
|
124
|
-
|
|
126
|
+
sourceMode: providerSourceMode,
|
|
125
127
|
userDir: appConfig.providerDir,
|
|
126
128
|
});
|
|
127
129
|
|
|
@@ -253,6 +255,10 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
253
255
|
await refreshProviderAvailability(providerType);
|
|
254
256
|
config.onStatusChange?.();
|
|
255
257
|
},
|
|
258
|
+
onProviderSourceConfigChanged: async () => {
|
|
259
|
+
await refreshProviderAvailability();
|
|
260
|
+
config.onStatusChange?.();
|
|
261
|
+
},
|
|
256
262
|
});
|
|
257
263
|
|
|
258
264
|
// 8. AgentStreamManager
|
|
@@ -312,6 +318,7 @@ export async function initDaemonComponents(config: DaemonInitConfig): Promise<Da
|
|
|
312
318
|
cdpManagers,
|
|
313
319
|
sessionRegistry,
|
|
314
320
|
detectedIdes: detectedIdesRef,
|
|
321
|
+
refreshProviderAvailability,
|
|
315
322
|
};
|
|
316
323
|
}
|
|
317
324
|
|
|
@@ -327,6 +334,9 @@ export async function startDaemonDevSupport(options: DaemonDevSupportOptions): P
|
|
|
327
334
|
instanceManager: options.components.instanceManager,
|
|
328
335
|
cliManager: options.components.cliManager,
|
|
329
336
|
logFn: options.logFn,
|
|
337
|
+
onProviderSourceConfigChanged: async () => {
|
|
338
|
+
await options.components.refreshProviderAvailability();
|
|
339
|
+
},
|
|
330
340
|
});
|
|
331
341
|
await devServer.start();
|
|
332
342
|
options.components.providerLoader.watch();
|
package/src/commands/handler.ts
CHANGED
|
@@ -46,6 +46,7 @@ export interface CommandContext {
|
|
|
46
46
|
instanceManager?: ProviderInstanceManager;
|
|
47
47
|
sessionRegistry?: SessionRegistry;
|
|
48
48
|
onProviderSettingChanged?: (providerType: string, key: string, value: any) => Promise<void> | void;
|
|
49
|
+
onProviderSourceConfigChanged?: () => Promise<void> | void;
|
|
49
50
|
}
|
|
50
51
|
|
|
51
52
|
/**
|
|
@@ -502,6 +503,8 @@ export class DaemonCommandHandler implements CommandHelpers {
|
|
|
502
503
|
// ─── Provider Settings (stream-commands.ts) ──────────
|
|
503
504
|
case 'get_provider_settings': return Stream.handleGetProviderSettings(this, args);
|
|
504
505
|
case 'set_provider_setting': return Stream.handleSetProviderSetting(this, args);
|
|
506
|
+
case 'get_provider_source_config': return Stream.handleGetProviderSourceConfig(this, args);
|
|
507
|
+
case 'set_provider_source_config': return Stream.handleSetProviderSourceConfig(this, args);
|
|
505
508
|
|
|
506
509
|
// ─── IDE Extension Settings (stream-commands.ts) ──────────
|
|
507
510
|
case 'get_ide_extensions': return Stream.handleGetIdeExtensions(this, args);
|
package/src/commands/router.ts
CHANGED
|
@@ -382,8 +382,9 @@ export class DaemonCommandRouter {
|
|
|
382
382
|
return { success: false, error: 'providerType required' };
|
|
383
383
|
}
|
|
384
384
|
|
|
385
|
-
const
|
|
386
|
-
const
|
|
385
|
+
const wantsAll = args?.all === true;
|
|
386
|
+
const offset = wantsAll ? 0 : Math.max(0, Number(args?.offset) || 0);
|
|
387
|
+
const limit = wantsAll ? Number.MAX_SAFE_INTEGER : Math.max(1, Math.min(100, Number(args?.limit) || 30));
|
|
387
388
|
const { sessions: historySessions, hasMore } = listSavedHistorySessions(providerType, { offset, limit });
|
|
388
389
|
const state = loadState();
|
|
389
390
|
const savedSessions = getSavedProviderSessions(state, { providerType, kind });
|
|
@@ -406,13 +407,13 @@ export class DaemonCommandRouter {
|
|
|
406
407
|
providerName: saved?.providerName || recent?.providerName || providerType,
|
|
407
408
|
kind: saved?.kind || recent?.kind || kind,
|
|
408
409
|
title: saved?.title || recent?.title || session.sessionTitle || session.preview || providerType,
|
|
409
|
-
workspace: saved?.workspace || recent?.workspace,
|
|
410
|
+
workspace: saved?.workspace || recent?.workspace || session.workspace,
|
|
410
411
|
currentModel: saved?.currentModel || recent?.currentModel,
|
|
411
412
|
preview: session.preview,
|
|
412
413
|
messageCount: session.messageCount,
|
|
413
414
|
firstMessageAt: session.firstMessageAt,
|
|
414
415
|
lastMessageAt: session.lastMessageAt,
|
|
415
|
-
canResume: !!(saved?.workspace || recent?.workspace) && canResumeById,
|
|
416
|
+
canResume: !!(saved?.workspace || recent?.workspace || session.workspace) && canResumeById,
|
|
416
417
|
};
|
|
417
418
|
}),
|
|
418
419
|
hasMore,
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import type { CommandResult, CommandHelpers } from './handler.js';
|
|
7
7
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
8
8
|
import type { ProviderInstance } from '../providers/provider-instance.js';
|
|
9
|
+
import { loadConfig, saveConfig } from '../config/config.js';
|
|
10
|
+
import { parseProviderSourceConfigUpdate } from '../config/provider-source-config.js';
|
|
9
11
|
import { getCliScriptCommand, parseCliScriptResult } from '../providers/cli-script-results.js';
|
|
10
12
|
import {
|
|
11
13
|
normalizeControlInvokeResult,
|
|
@@ -107,6 +109,45 @@ export async function handleSetProviderSetting(h: CommandHelpers, args: any): Pr
|
|
|
107
109
|
return { success: false, error: `Failed to set ${providerType}.${key} — invalid key, value, or not a public setting` };
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
export function handleGetProviderSourceConfig(h: CommandHelpers, _args: any): CommandResult {
|
|
113
|
+
const loader = h.ctx.providerLoader as ProviderLoader | undefined;
|
|
114
|
+
if (!loader) return { success: false, error: 'providerLoader not available' };
|
|
115
|
+
return { success: true, ...loader.getSourceConfig() };
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function handleSetProviderSourceConfig(h: CommandHelpers, args: any): Promise<CommandResult> {
|
|
119
|
+
const loader = h.ctx.providerLoader as ProviderLoader | undefined;
|
|
120
|
+
if (!loader) return { success: false, error: 'providerLoader not available' };
|
|
121
|
+
|
|
122
|
+
const parsed = parseProviderSourceConfigUpdate(args || {});
|
|
123
|
+
if ('error' in parsed) {
|
|
124
|
+
return { success: false, error: parsed.error };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const currentConfig = loadConfig();
|
|
128
|
+
const nextConfig = {
|
|
129
|
+
...currentConfig,
|
|
130
|
+
...(parsed.updates.providerSourceMode ? { providerSourceMode: parsed.updates.providerSourceMode } : {}),
|
|
131
|
+
...(Object.prototype.hasOwnProperty.call(parsed.updates, 'providerDir') ? { providerDir: parsed.updates.providerDir } : {}),
|
|
132
|
+
};
|
|
133
|
+
saveConfig(nextConfig);
|
|
134
|
+
|
|
135
|
+
const sourceConfig = loader.applySourceConfig({
|
|
136
|
+
sourceMode: nextConfig.providerSourceMode,
|
|
137
|
+
userDir: Object.prototype.hasOwnProperty.call(parsed.updates, 'providerDir') ? parsed.updates.providerDir : loader.getSourceConfig().explicitProviderDir || undefined,
|
|
138
|
+
});
|
|
139
|
+
loader.reload();
|
|
140
|
+
loader.registerToDetector();
|
|
141
|
+
await h.ctx.onProviderSourceConfigChanged?.();
|
|
142
|
+
|
|
143
|
+
LOG.info(
|
|
144
|
+
'Command',
|
|
145
|
+
`[set_provider_source_config] mode=${sourceConfig.sourceMode} explicitProviderDir=${sourceConfig.explicitProviderDir || '-'} userDir=${sourceConfig.userDir}`,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
return { success: true, reloaded: true, ...sourceConfig };
|
|
149
|
+
}
|
|
150
|
+
|
|
110
151
|
// ─── Extension Script Execution (Model/Mode) ─────
|
|
111
152
|
|
|
112
153
|
function normalizeProviderScriptArgs(args: any): Record<string, any> {
|
|
@@ -27,6 +27,7 @@ interface HistoryMessage {
|
|
|
27
27
|
instanceId?: string; // IDE instance UUID (distinguishes windows of the same agent type)
|
|
28
28
|
historySessionId?: string; // Persistent provider-side conversation/session key
|
|
29
29
|
sessionTitle?: string;
|
|
30
|
+
workspace?: string; // Working directory at session start (kind: 'session_start' only)
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
const CODEX_STARTER_PROMPT_RE = /^(?:[›❯]\s*)?(?:Find and fix a bug in @filename|Improve documentation in @filename|Write tests for @filename|Explain this codebase|Summarize recent commits|Implement \{feature\}|Use \/skills(?: to list available skills)?|Run \/review on my current changes)$/i;
|
|
@@ -123,6 +124,7 @@ export interface SavedHistorySessionSummary {
|
|
|
123
124
|
firstMessageAt: number;
|
|
124
125
|
lastMessageAt: number;
|
|
125
126
|
preview?: string;
|
|
127
|
+
workspace?: string;
|
|
126
128
|
}
|
|
127
129
|
|
|
128
130
|
export class ChatHistoryWriter {
|
|
@@ -325,6 +327,37 @@ export class ChatHistoryWriter {
|
|
|
325
327
|
);
|
|
326
328
|
}
|
|
327
329
|
|
|
330
|
+
writeSessionStart(
|
|
331
|
+
agentType: string,
|
|
332
|
+
historySessionId: string,
|
|
333
|
+
workspace: string,
|
|
334
|
+
instanceId?: string,
|
|
335
|
+
): void {
|
|
336
|
+
const id = String(historySessionId || '').trim();
|
|
337
|
+
const ws = String(workspace || '').trim();
|
|
338
|
+
if (!id || !ws) return;
|
|
339
|
+
try {
|
|
340
|
+
const dir = path.join(HISTORY_DIR, this.sanitize(agentType));
|
|
341
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
342
|
+
const date = new Date().toISOString().slice(0, 10);
|
|
343
|
+
const filePath = path.join(dir, `${this.sanitize(id)}_${date}.jsonl`);
|
|
344
|
+
const record: HistoryMessage = {
|
|
345
|
+
ts: new Date().toISOString(),
|
|
346
|
+
receivedAt: Date.now(),
|
|
347
|
+
role: 'system',
|
|
348
|
+
kind: 'session_start',
|
|
349
|
+
content: ws,
|
|
350
|
+
agent: agentType,
|
|
351
|
+
instanceId,
|
|
352
|
+
historySessionId: id,
|
|
353
|
+
workspace: ws,
|
|
354
|
+
};
|
|
355
|
+
fs.appendFileSync(filePath, JSON.stringify(record) + '\n', 'utf-8');
|
|
356
|
+
} catch {
|
|
357
|
+
// Ignore — must not affect main functionality
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
328
361
|
promoteHistorySession(
|
|
329
362
|
agentType: string,
|
|
330
363
|
previousHistorySessionId: string,
|
|
@@ -606,6 +639,7 @@ export function listSavedHistorySessions(
|
|
|
606
639
|
let lastMessageAt = 0;
|
|
607
640
|
let sessionTitle = '';
|
|
608
641
|
let preview = '';
|
|
642
|
+
let workspace = '';
|
|
609
643
|
|
|
610
644
|
for (const file of files.sort()) {
|
|
611
645
|
const filePath = path.join(dir, file);
|
|
@@ -619,6 +653,10 @@ export function listSavedHistorySessions(
|
|
|
619
653
|
parsed = null;
|
|
620
654
|
}
|
|
621
655
|
if (!parsed || parsed.historySessionId !== historySessionId) continue;
|
|
656
|
+
if (parsed.kind === 'session_start') {
|
|
657
|
+
if (!workspace && parsed.workspace) workspace = parsed.workspace;
|
|
658
|
+
continue;
|
|
659
|
+
}
|
|
622
660
|
messageCount += 1;
|
|
623
661
|
if (!firstMessageAt || parsed.receivedAt < firstMessageAt) firstMessageAt = parsed.receivedAt;
|
|
624
662
|
if (!lastMessageAt || parsed.receivedAt > lastMessageAt) lastMessageAt = parsed.receivedAt;
|
|
@@ -635,6 +673,7 @@ export function listSavedHistorySessions(
|
|
|
635
673
|
firstMessageAt,
|
|
636
674
|
lastMessageAt,
|
|
637
675
|
preview: preview || undefined,
|
|
676
|
+
workspace: workspace || undefined,
|
|
638
677
|
});
|
|
639
678
|
}
|
|
640
679
|
|
package/src/config/config.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export type { WorkspaceEntry } from './workspaces.js';
|
|
|
8
8
|
export type { RecentActivityEntry } from './recent-activity.js';
|
|
9
9
|
export type { SavedProviderSessionEntry } from './saved-sessions.js';
|
|
10
10
|
export type { DaemonState } from './state-store.js';
|
|
11
|
+
export type ProviderSourceMode = 'normal' | 'no-upstream';
|
|
12
|
+
export declare function resolveProviderSourceMode(providerSourceMode: unknown, legacyDisableUpstream: unknown): ProviderSourceMode;
|
|
11
13
|
export interface ADHDevConfig {
|
|
12
14
|
serverUrl: string;
|
|
13
15
|
selectedIde: string | null;
|
|
@@ -45,6 +47,7 @@ export interface ADHDevConfig {
|
|
|
45
47
|
}>;
|
|
46
48
|
}>;
|
|
47
49
|
disableUpstream?: boolean;
|
|
50
|
+
providerSourceMode?: ProviderSourceMode;
|
|
48
51
|
providerDir?: string;
|
|
49
52
|
/**
|
|
50
53
|
* Browser terminal sizing behavior for dashboard CLI panes.
|
package/src/config/config.ts
CHANGED
|
@@ -14,6 +14,18 @@ export type { RecentActivityEntry } from './recent-activity.js';
|
|
|
14
14
|
export type { SavedProviderSessionEntry } from './saved-sessions.js';
|
|
15
15
|
export type { DaemonState } from './state-store.js';
|
|
16
16
|
|
|
17
|
+
export type ProviderSourceMode = 'normal' | 'no-upstream';
|
|
18
|
+
|
|
19
|
+
export function resolveProviderSourceMode(
|
|
20
|
+
providerSourceMode: unknown,
|
|
21
|
+
legacyDisableUpstream: unknown,
|
|
22
|
+
): ProviderSourceMode {
|
|
23
|
+
if (providerSourceMode === 'normal' || providerSourceMode === 'no-upstream') {
|
|
24
|
+
return providerSourceMode;
|
|
25
|
+
}
|
|
26
|
+
return legacyDisableUpstream === true ? 'no-upstream' : 'normal';
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
export interface ADHDevConfig {
|
|
18
30
|
// Server connection
|
|
19
31
|
serverUrl: string;
|
|
@@ -81,9 +93,13 @@ export interface ADHDevConfig {
|
|
|
81
93
|
|
|
82
94
|
// Disable upstream provider auto-download (use builtin only)
|
|
83
95
|
// Controllable from CLI (--no-upstream) and dashboard (machine page)
|
|
96
|
+
// Deprecated legacy boolean; prefer providerSourceMode.
|
|
84
97
|
disableUpstream?: boolean;
|
|
85
98
|
|
|
86
|
-
//
|
|
99
|
+
// Explicit machine-level provider source policy.
|
|
100
|
+
providerSourceMode?: ProviderSourceMode;
|
|
101
|
+
|
|
102
|
+
// Optional explicit provider override root (for example a local adhdev-providers checkout)
|
|
87
103
|
providerDir?: string;
|
|
88
104
|
|
|
89
105
|
/**
|
|
@@ -113,7 +129,7 @@ const DEFAULT_CONFIG: ADHDevConfig = {
|
|
|
113
129
|
registeredMachineId: undefined,
|
|
114
130
|
providerSettings: {},
|
|
115
131
|
ideSettings: {},
|
|
116
|
-
|
|
132
|
+
providerSourceMode: 'normal',
|
|
117
133
|
terminalSizingMode: 'measured',
|
|
118
134
|
};
|
|
119
135
|
|
|
@@ -164,7 +180,7 @@ function normalizeConfig(raw: unknown): ADHDevConfig & { activeWorkspaceId?: str
|
|
|
164
180
|
registeredMachineId: asOptionalString(parsed.registeredMachineId),
|
|
165
181
|
providerSettings: isPlainObject(parsed.providerSettings) ? parsed.providerSettings : {},
|
|
166
182
|
ideSettings: isPlainObject(parsed.ideSettings) ? parsed.ideSettings : {},
|
|
167
|
-
|
|
183
|
+
providerSourceMode: resolveProviderSourceMode(parsed.providerSourceMode, parsed.disableUpstream),
|
|
168
184
|
providerDir: asOptionalString(parsed.providerDir),
|
|
169
185
|
terminalSizingMode: parsed.terminalSizingMode === 'fit' ? 'fit' : 'measured',
|
|
170
186
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { ProviderSourceMode } from './config.js'
|
|
2
|
+
|
|
3
|
+
export interface ProviderSourceConfigSnapshot {
|
|
4
|
+
sourceMode: ProviderSourceMode
|
|
5
|
+
disableUpstream: boolean
|
|
6
|
+
explicitProviderDir: string | null
|
|
7
|
+
userDir: string
|
|
8
|
+
upstreamDir: string
|
|
9
|
+
providerRoots: string[]
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface ProviderSourceConfigUpdate {
|
|
13
|
+
providerSourceMode?: ProviderSourceMode
|
|
14
|
+
providerDir?: string | undefined
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function normalizeProviderDir(value: unknown): string | undefined {
|
|
18
|
+
if (typeof value !== 'string') return undefined
|
|
19
|
+
const trimmed = value.trim()
|
|
20
|
+
return trimmed ? trimmed : undefined
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function parseProviderSourceConfigUpdate(input: {
|
|
24
|
+
providerSourceMode?: unknown
|
|
25
|
+
providerDir?: unknown
|
|
26
|
+
}): { ok: true; updates: ProviderSourceConfigUpdate } | { ok: false; error: string } {
|
|
27
|
+
const updates: ProviderSourceConfigUpdate = {}
|
|
28
|
+
|
|
29
|
+
if (Object.prototype.hasOwnProperty.call(input, 'providerSourceMode')) {
|
|
30
|
+
const { providerSourceMode } = input
|
|
31
|
+
if (providerSourceMode !== 'normal' && providerSourceMode !== 'no-upstream') {
|
|
32
|
+
return { ok: false, error: "providerSourceMode must be 'normal' or 'no-upstream'" }
|
|
33
|
+
}
|
|
34
|
+
updates.providerSourceMode = providerSourceMode
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (Object.prototype.hasOwnProperty.call(input, 'providerDir')) {
|
|
38
|
+
updates.providerDir = normalizeProviderDir(input.providerDir)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { ok: true, updates }
|
|
42
|
+
}
|
|
@@ -141,19 +141,6 @@ export function resolveAutoImplWritableProviderDir(ctx: DevServerContext,
|
|
|
141
141
|
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
142
142
|
}
|
|
143
143
|
|
|
144
|
-
try {
|
|
145
|
-
const providerData = JSON.parse(fs.readFileSync(providerJson, 'utf-8'));
|
|
146
|
-
if (providerData.disableUpstream !== true) {
|
|
147
|
-
providerData.disableUpstream = true;
|
|
148
|
-
fs.writeFileSync(providerJson, JSON.stringify(providerData, null, 2));
|
|
149
|
-
}
|
|
150
|
-
} catch (error) {
|
|
151
|
-
return {
|
|
152
|
-
dir: null,
|
|
153
|
-
reason: `Failed to update provider.json in writable provider directory: ${(error as Error).message}`,
|
|
154
|
-
};
|
|
155
|
-
}
|
|
156
|
-
|
|
157
144
|
return { dir: desiredDir };
|
|
158
145
|
}
|
|
159
146
|
|
|
@@ -20,6 +20,7 @@ export interface DevServerContext {
|
|
|
20
20
|
readonly cdpManagers: Map<string, DaemonCdpManager>;
|
|
21
21
|
readonly instanceManager: ProviderInstanceManager | null;
|
|
22
22
|
readonly cliManager: DaemonCliManager | null;
|
|
23
|
+
readonly onProviderSourceConfigChanged?: (() => Promise<void> | void) | null;
|
|
23
24
|
|
|
24
25
|
// Utilities
|
|
25
26
|
getCdp(ideType?: string): DaemonCdpManager | null;
|
package/src/daemon/dev-server.ts
CHANGED
|
@@ -22,6 +22,8 @@ import * as os from 'os';
|
|
|
22
22
|
import type { ProviderLoader } from '../providers/provider-loader.js';
|
|
23
23
|
import type { ProviderCategory, ProviderModule, ProviderScripts, ProviderSettingDef } from '../providers/contracts.js';
|
|
24
24
|
import { validateProviderDefinition } from '../providers/provider-schema.js';
|
|
25
|
+
import { loadConfig, saveConfig } from '../config/config.js';
|
|
26
|
+
import { parseProviderSourceConfigUpdate } from '../config/provider-source-config.js';
|
|
25
27
|
import type { ChildProcess } from 'child_process';
|
|
26
28
|
import type { DaemonCdpManager } from '../cdp/manager.js';
|
|
27
29
|
import type { ProviderInstanceManager } from '../providers/provider-instance-manager.js';
|
|
@@ -100,6 +102,7 @@ export class DevServer implements DevServerContext {
|
|
|
100
102
|
public cdpManagers: Map<string, DaemonCdpManager>;
|
|
101
103
|
public instanceManager: ProviderInstanceManager | null;
|
|
102
104
|
public cliManager: DaemonCliManager | null;
|
|
105
|
+
public onProviderSourceConfigChanged: (() => Promise<void> | void) | null;
|
|
103
106
|
private logFn: (msg: string) => void;
|
|
104
107
|
private sseClients: http.ServerResponse[] = [];
|
|
105
108
|
private watchScriptPath: string | null = null;
|
|
@@ -120,11 +123,13 @@ export class DevServer implements DevServerContext {
|
|
|
120
123
|
instanceManager?: ProviderInstanceManager;
|
|
121
124
|
cliManager?: DaemonCliManager;
|
|
122
125
|
logFn?: (msg: string) => void;
|
|
126
|
+
onProviderSourceConfigChanged?: () => Promise<void> | void;
|
|
123
127
|
}) {
|
|
124
128
|
this.providerLoader = options.providerLoader;
|
|
125
129
|
this.cdpManagers = options.cdpManagers;
|
|
126
130
|
this.instanceManager = options.instanceManager || null;
|
|
127
131
|
this.cliManager = options.cliManager || null;
|
|
132
|
+
this.onProviderSourceConfigChanged = options.onProviderSourceConfigChanged || null;
|
|
128
133
|
this.logFn = options.logFn || LOG.forComponent('DevServer').asLogFn();
|
|
129
134
|
}
|
|
130
135
|
|
|
@@ -140,6 +145,8 @@ export class DevServer implements DevServerContext {
|
|
|
140
145
|
}[] = [
|
|
141
146
|
// Static routes
|
|
142
147
|
{ method: 'GET', pattern: '/api/providers', handler: (q, s) => this.handleListProviders(q, s) },
|
|
148
|
+
{ method: 'GET', pattern: '/api/providers/source-config', handler: (q, s) => this.handleGetProviderSourceConfig(q, s) },
|
|
149
|
+
{ method: 'POST', pattern: '/api/providers/source-config', handler: (q, s) => this.handleSetProviderSourceConfig(q, s) },
|
|
143
150
|
{ method: 'GET', pattern: '/api/providers/versions', handler: (q, s) => this.handleDetectVersions(q, s) },
|
|
144
151
|
{ method: 'POST', pattern: '/api/providers/reload', handler: (q, s) => this.handleReload(q, s) },
|
|
145
152
|
{ method: 'POST', pattern: '/api/cdp/evaluate', handler: (q, s) => this.handleCdpEvaluate(q, s) },
|
|
@@ -273,7 +280,38 @@ export class DevServer implements DevServerContext {
|
|
|
273
280
|
|
|
274
281
|
private async handleListProviders(_req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
275
282
|
const providers = this.providerLoader.getAll().map(toProviderListEntry);
|
|
276
|
-
this.json(res, 200, { providers, count: providers.length });
|
|
283
|
+
this.json(res, 200, { providers, count: providers.length, sourceConfig: this.providerLoader.getSourceConfig() });
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
private async handleGetProviderSourceConfig(_req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
287
|
+
this.json(res, 200, { success: true, sourceConfig: this.providerLoader.getSourceConfig() });
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
private async handleSetProviderSourceConfig(req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
291
|
+
const body = await this.readBody(req);
|
|
292
|
+
const parsed = parseProviderSourceConfigUpdate(body || {});
|
|
293
|
+
if (!parsed.ok) {
|
|
294
|
+
this.json(res, 400, { success: false, error: parsed.error });
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const currentConfig = loadConfig();
|
|
299
|
+
const nextConfig = {
|
|
300
|
+
...currentConfig,
|
|
301
|
+
...(parsed.updates.providerSourceMode ? { providerSourceMode: parsed.updates.providerSourceMode } : {}),
|
|
302
|
+
...(Object.prototype.hasOwnProperty.call(parsed.updates, 'providerDir') ? { providerDir: parsed.updates.providerDir } : {}),
|
|
303
|
+
};
|
|
304
|
+
saveConfig(nextConfig);
|
|
305
|
+
|
|
306
|
+
const sourceConfig = this.providerLoader.applySourceConfig({
|
|
307
|
+
sourceMode: nextConfig.providerSourceMode,
|
|
308
|
+
userDir: Object.prototype.hasOwnProperty.call(parsed.updates, 'providerDir') ? parsed.updates.providerDir : this.providerLoader.getSourceConfig().explicitProviderDir || undefined,
|
|
309
|
+
});
|
|
310
|
+
this.providerLoader.reload();
|
|
311
|
+
this.providerLoader.registerToDetector();
|
|
312
|
+
await this.onProviderSourceConfigChanged?.();
|
|
313
|
+
|
|
314
|
+
this.json(res, 200, { success: true, reloaded: true, sourceConfig });
|
|
277
315
|
}
|
|
278
316
|
|
|
279
317
|
private async handleProviderConfig(type: string, _req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
@@ -994,27 +1032,14 @@ export class DevServer implements DevServerContext {
|
|
|
994
1032
|
this.log(`Auto-implement writable copy created: ${desiredDir}`);
|
|
995
1033
|
}
|
|
996
1034
|
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
try {
|
|
1003
|
-
const providerData = JSON.parse(fs.readFileSync(providerJson, 'utf-8'));
|
|
1004
|
-
if (providerData.disableUpstream !== true) {
|
|
1005
|
-
providerData.disableUpstream = true;
|
|
1006
|
-
fs.writeFileSync(providerJson, JSON.stringify(providerData, null, 2));
|
|
1007
|
-
}
|
|
1008
|
-
} catch (error) {
|
|
1009
|
-
return {
|
|
1010
|
-
dir: null,
|
|
1011
|
-
reason: `Failed to update provider.json in writable provider directory: ${(error as Error).message}`,
|
|
1012
|
-
};
|
|
1013
|
-
}
|
|
1014
|
-
|
|
1015
|
-
return { dir: desiredDir };
|
|
1035
|
+
const providerJson = path.join(desiredDir, 'provider.json');
|
|
1036
|
+
if (!fs.existsSync(providerJson)) {
|
|
1037
|
+
return { dir: null, reason: `provider.json not found in writable provider directory: ${desiredDir}` };
|
|
1016
1038
|
}
|
|
1017
1039
|
|
|
1040
|
+
return { dir: desiredDir };
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1018
1043
|
|
|
1019
1044
|
private async handleAutoImplement(type: string, req: http.IncomingMessage, res: http.ServerResponse): Promise<void> {
|
|
1020
1045
|
return handleAutoImplement(this, type, req, res);
|
package/src/index.ts
CHANGED
|
@@ -195,6 +195,8 @@ export { IdeProviderInstance } from './providers/ide-provider-instance.js';
|
|
|
195
195
|
export { CliProviderInstance } from './providers/cli-provider-instance.js';
|
|
196
196
|
export { AcpProviderInstance } from './providers/acp-provider-instance.js';
|
|
197
197
|
export type { ProviderModule, CdpTargetFilter, ProviderResumeCapability, InputEnvelope, InputPart, MessagePart, ControlListResult, ControlSetResult, ControlInvokeResult } from './providers/contracts.js';
|
|
198
|
+
export type { ProviderSourceConfigSnapshot, ProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
199
|
+
export { parseProviderSourceConfigUpdate } from './config/provider-source-config.js';
|
|
198
200
|
export { normalizeInputEnvelope, normalizeMessageParts, flattenMessageParts } from './providers/io-contracts.js';
|
|
199
201
|
export { VersionArchive, detectAllVersions } from './providers/version-archive.js';
|
|
200
202
|
export type { ProviderVersionInfo, VersionHistory } from './providers/version-archive.js';
|
|
@@ -55,6 +55,9 @@ export function getForcedNewSessionScriptName(
|
|
|
55
55
|
const controls = Array.isArray((provider as any).controls) ? (provider as any).controls : [];
|
|
56
56
|
for (const control of controls) {
|
|
57
57
|
if (control?.type !== 'action') continue;
|
|
58
|
+
if (typeof control?.confirmTitle === 'string' && control.confirmTitle.trim()) continue;
|
|
59
|
+
if (typeof control?.confirmMessage === 'string' && control.confirmMessage.trim()) continue;
|
|
60
|
+
if (typeof control?.confirmLabel === 'string' && control.confirmLabel.trim()) continue;
|
|
58
61
|
const invokeScript = typeof control?.invokeScript === 'string' ? control.invokeScript.trim() : '';
|
|
59
62
|
if (!invokeScript) continue;
|
|
60
63
|
const controlId = typeof control?.id === 'string' ? control.id.trim() : '';
|
|
@@ -66,6 +69,26 @@ export function getForcedNewSessionScriptName(
|
|
|
66
69
|
return null;
|
|
67
70
|
}
|
|
68
71
|
|
|
72
|
+
export async function waitForCliAdapterReady(
|
|
73
|
+
adapter: { isReady?: () => boolean; getStatus?: () => { status?: string } },
|
|
74
|
+
options?: { timeoutMs?: number; pollMs?: number },
|
|
75
|
+
): Promise<void> {
|
|
76
|
+
const timeoutMs = Math.max(100, options?.timeoutMs ?? 15_000);
|
|
77
|
+
const pollMs = Math.max(10, options?.pollMs ?? 50);
|
|
78
|
+
const deadline = Date.now() + timeoutMs;
|
|
79
|
+
|
|
80
|
+
while (Date.now() < deadline) {
|
|
81
|
+
if (adapter?.isReady?.()) return;
|
|
82
|
+
const status = adapter?.getStatus?.()?.status;
|
|
83
|
+
if (status === 'stopped') {
|
|
84
|
+
throw new Error('CLI runtime stopped before it became ready');
|
|
85
|
+
}
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, pollMs));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
throw new Error(`CLI runtime did not become ready within ${timeoutMs}ms`);
|
|
90
|
+
}
|
|
91
|
+
|
|
69
92
|
export class CliProviderInstance implements ProviderInstance {
|
|
70
93
|
readonly type: string;
|
|
71
94
|
readonly category = 'cli' as const;
|
|
@@ -427,6 +450,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
427
450
|
if (!scriptName) return;
|
|
428
451
|
|
|
429
452
|
LOG.info('CLI', `[${this.type}] forcing fresh session launch via script: ${scriptName}`);
|
|
453
|
+
await waitForCliAdapterReady(this.adapter);
|
|
430
454
|
const raw = await this.adapter.invokeScript(scriptName, {});
|
|
431
455
|
const parsed = parseCliScriptResult(raw);
|
|
432
456
|
if (!parsed.success) {
|
|
@@ -819,6 +843,7 @@ export class CliProviderInstance implements ProviderInstance {
|
|
|
819
843
|
const previousProviderSessionId = this.providerSessionId;
|
|
820
844
|
this.providerSessionId = nextSessionId;
|
|
821
845
|
this.historyWriter.promoteHistorySession(this.type, previousHistorySessionId, nextSessionId);
|
|
846
|
+
this.historyWriter.writeSessionStart(this.type, nextSessionId, this.workingDir, this.instanceId);
|
|
822
847
|
this.adapter.updateRuntimeMeta({ providerSessionId: nextSessionId });
|
|
823
848
|
this.onProviderSessionResolved?.({
|
|
824
849
|
instanceId: this.instanceId,
|
|
@@ -414,6 +414,7 @@ export interface ProviderModule {
|
|
|
414
414
|
/** How the CLI adapter decides when to submit typed input */
|
|
415
415
|
submitStrategy?: 'wait_for_echo' | 'immediate';
|
|
416
416
|
/** Keep this provider out of the upstream auto-updated bundle */
|
|
417
|
+
/** @deprecated Machine-level provider source policy now lives in config.providerSourceMode. Local overrides shadow upstream by root precedence and should not rely on provider-level disableUpstream. */
|
|
417
418
|
disableUpstream?: boolean;
|
|
418
419
|
approvalKeys?: Record<number, string>;
|
|
419
420
|
patterns?: {
|