@adhdev/daemon-core 0.9.82-rc.136 → 0.9.82-rc.137
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/cli-adapters/cli-script-runner.d.ts +45 -0
- package/dist/cli-adapters/cli-state-engine.d.ts +154 -0
- package/dist/cli-adapters/provider-cli-adapter.d.ts +73 -74
- package/dist/cli-adapters/provider-cli-shared.d.ts +4 -0
- package/dist/config/chat-history.d.ts +1 -0
- package/dist/index.d.ts +3 -3
- package/dist/index.js +2591 -1966
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2594 -1974
- package/dist/index.mjs.map +1 -1
- package/dist/mesh/beads-db.d.ts +54 -0
- package/dist/mesh/mesh-active-work.d.ts +7 -1
- package/dist/mesh/mesh-events.d.ts +10 -4
- package/dist/mesh/mesh-ledger.d.ts +21 -1
- package/dist/mesh/mesh-refine-status.d.ts +2 -3
- package/dist/mesh/mesh-work-queue.d.ts +17 -0
- package/dist/mesh/worktree-bootstrap-config.d.ts +2 -4
- package/dist/repo-mesh-types.d.ts +5 -0
- package/package.json +1 -1
- package/src/cli-adapters/cli-script-runner.ts +145 -0
- package/src/cli-adapters/cli-state-engine.ts +957 -0
- package/src/cli-adapters/provider-cli-adapter.d.ts +0 -1
- package/src/cli-adapters/provider-cli-adapter.ts +365 -1397
- package/src/cli-adapters/provider-cli-shared.ts +4 -0
- package/src/commands/chat-commands.ts +17 -1
- package/src/commands/router.ts +8 -0
- package/src/config/chat-history.ts +7 -3
- package/src/git/git-worktree.ts +8 -1
- package/src/index.ts +3 -2
- package/src/mesh/beads-db.ts +305 -2
- package/src/mesh/coordinator-prompt.ts +12 -17
- package/src/mesh/mesh-active-work.ts +162 -59
- package/src/mesh/mesh-events.ts +198 -53
- package/src/mesh/mesh-ledger.ts +321 -105
- package/src/mesh/mesh-refine-status.ts +2 -3
- package/src/mesh/mesh-work-queue.ts +116 -120
- package/src/mesh/worktree-bootstrap-config.ts +17 -4
- package/src/providers/provider-schema.ts +2 -0
- package/src/repo-mesh-types.ts +10 -0
package/dist/mesh/beads-db.d.ts
CHANGED
|
@@ -2,17 +2,71 @@ import type { MeshTaskStatus, MeshWorkQueueEntry } from './mesh-work-queue.js';
|
|
|
2
2
|
export declare class BeadsDB {
|
|
3
3
|
private static instance;
|
|
4
4
|
private readonly db;
|
|
5
|
+
private readonly dbPath;
|
|
5
6
|
private readonly migratedMeshIds;
|
|
7
|
+
private fingerprintSweepCounter;
|
|
8
|
+
private walWriteCounter;
|
|
9
|
+
private static readonly WAL_CHECK_INTERVAL;
|
|
10
|
+
private static readonly WAL_MAX_BYTES;
|
|
6
11
|
private constructor();
|
|
7
12
|
static getInstance(): BeadsDB;
|
|
8
13
|
static resetForTests(): void;
|
|
9
14
|
close(): void;
|
|
10
15
|
transaction<T>(fn: () => T): T;
|
|
11
16
|
private migrate;
|
|
17
|
+
hasCompletionFingerprint(fingerprint: string): boolean;
|
|
18
|
+
recordCompletionFingerprint(fingerprint: string, ttlMs: number): void;
|
|
19
|
+
sweepExpiredFingerprints(): void;
|
|
20
|
+
private maybeCheckpointWal;
|
|
12
21
|
private ensureLegacyQueueMigrated;
|
|
13
22
|
getQueueEntries(meshId: string, statuses?: MeshTaskStatus[]): MeshWorkQueueEntry[];
|
|
14
23
|
getQueueRevision(meshId: string): string;
|
|
15
24
|
replaceQueue(meshId: string, queue: MeshWorkQueueEntry[]): void;
|
|
16
25
|
deleteQueue(meshId: string): void;
|
|
26
|
+
insertQueueEntry(entry: MeshWorkQueueEntry): void;
|
|
27
|
+
updateQueueEntry(entry: MeshWorkQueueEntry): void;
|
|
28
|
+
findQueueEntryById(meshId: string, id: string): MeshWorkQueueEntry | null;
|
|
29
|
+
hasActiveAssignment(meshId: string, sessionId: string, nodeId: string): boolean;
|
|
30
|
+
claimNextQueueTask(meshId: string, nodeId: string, sessionId: string): MeshWorkQueueEntry | null;
|
|
31
|
+
getQueueStatsByStatus(meshId: string): {
|
|
32
|
+
status: string;
|
|
33
|
+
count: number;
|
|
34
|
+
}[];
|
|
35
|
+
getActiveAssignmentDetails(meshId: string): Array<{
|
|
36
|
+
id: string;
|
|
37
|
+
nodeId?: string;
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
message: string;
|
|
40
|
+
}>;
|
|
41
|
+
findAssignedBySession(meshId: string, sessionId: string, occurredAtIso?: string): MeshWorkQueueEntry | null;
|
|
17
42
|
private toRow;
|
|
43
|
+
insertDirectDispatch(entry: {
|
|
44
|
+
taskId: string;
|
|
45
|
+
meshId: string;
|
|
46
|
+
nodeId?: string;
|
|
47
|
+
sessionId?: string;
|
|
48
|
+
providerType?: string;
|
|
49
|
+
message: string;
|
|
50
|
+
taskMode?: string;
|
|
51
|
+
via: string;
|
|
52
|
+
dispatchedToIdleSession?: boolean;
|
|
53
|
+
dispatchedAt: string;
|
|
54
|
+
}): void;
|
|
55
|
+
getActiveDirectDispatches(meshId: string): Array<{
|
|
56
|
+
taskId: string;
|
|
57
|
+
meshId: string;
|
|
58
|
+
nodeId: string | null;
|
|
59
|
+
sessionId: string | null;
|
|
60
|
+
providerType: string | null;
|
|
61
|
+
message: string;
|
|
62
|
+
taskMode: string | null;
|
|
63
|
+
via: string;
|
|
64
|
+
status: string;
|
|
65
|
+
dispatchedToIdleSession: boolean;
|
|
66
|
+
dispatchedAt: string;
|
|
67
|
+
updatedAt: string;
|
|
68
|
+
}>;
|
|
69
|
+
updateDirectDispatchStatus(meshId: string, sessionId: string, status: 'acked' | 'completed' | 'failed' | 'stale'): void;
|
|
70
|
+
cleanupTerminalDirectDispatches(olderThanMs: number): void;
|
|
71
|
+
markStaleDirectDispatches(meshId: string, olderThanMs: number): void;
|
|
18
72
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MeshLedgerEntry } from './mesh-ledger.js';
|
|
2
|
-
import type { MeshWorkQueueEntry } from './mesh-work-queue.js';
|
|
2
|
+
import type { MeshWorkQueueEntry, DirectDispatchRecord } from './mesh-work-queue.js';
|
|
3
3
|
export type MeshActiveWorkSource = 'queue' | 'direct';
|
|
4
4
|
export type MeshActiveWorkStatus = 'pending' | 'assigned' | 'generating' | 'idle' | 'failed' | 'awaiting_approval';
|
|
5
5
|
export interface MeshActiveWorkRecord {
|
|
@@ -64,6 +64,12 @@ export interface BuildMeshActiveWorkOptions {
|
|
|
64
64
|
meshId: string;
|
|
65
65
|
queue?: MeshWorkQueueEntry[];
|
|
66
66
|
ledgerEntries?: MeshLedgerEntry[];
|
|
67
|
+
/**
|
|
68
|
+
* Active direct dispatches from BeadsDB. When provided, these are used instead of
|
|
69
|
+
* scanning ledger entries for direct dispatches — eliminates the O(n_ledger) scan.
|
|
70
|
+
* Falls back to ledger scanning when not provided.
|
|
71
|
+
*/
|
|
72
|
+
directDispatches?: DirectDispatchRecord[];
|
|
67
73
|
nodes?: any[];
|
|
68
74
|
now?: number;
|
|
69
75
|
/** Include terminal direct rows (idle/failed) for handoff/recent-work surfaces. Defaults false. */
|
|
@@ -8,14 +8,20 @@ export interface PendingMeshCoordinatorEvent {
|
|
|
8
8
|
metadataEvent: Record<string, unknown>;
|
|
9
9
|
coordinatorMessage?: string;
|
|
10
10
|
queuedAt: number;
|
|
11
|
+
/**
|
|
12
|
+
* When set, this event is intended for a specific coordinator daemon.
|
|
13
|
+
* Coordinators on other daemons should ignore it during drain.
|
|
14
|
+
* Absent on legacy events — treated as broadcast to any coordinator.
|
|
15
|
+
*/
|
|
16
|
+
targetCoordinatorDaemonId?: string;
|
|
11
17
|
}
|
|
12
18
|
export declare function queuePendingMeshCoordinatorEvent(event: PendingMeshCoordinatorEvent): boolean;
|
|
13
19
|
/** Drain and return all pending coordinator events for meshId, removing them from disk. */
|
|
14
|
-
export declare function drainPendingMeshCoordinatorEvents(meshId?: string): PendingMeshCoordinatorEvent[];
|
|
20
|
+
export declare function drainPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): PendingMeshCoordinatorEvent[];
|
|
15
21
|
/** Peek at pending coordinator events without draining (non-destructive). */
|
|
16
|
-
export declare function getPendingMeshCoordinatorEvents(meshId?: string): readonly PendingMeshCoordinatorEvent[];
|
|
17
|
-
/** Explicitly clear all pending coordinator events for a mesh. */
|
|
18
|
-
export declare function clearPendingMeshCoordinatorEvents(meshId?: string): void;
|
|
22
|
+
export declare function getPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): readonly PendingMeshCoordinatorEvent[];
|
|
23
|
+
/** Explicitly clear all pending coordinator events for a mesh (and coordinator if scoped). */
|
|
24
|
+
export declare function clearPendingMeshCoordinatorEvents(meshId?: string, coordinatorDaemonId?: string): void;
|
|
19
25
|
export declare function tryAssignQueueTask(components: DaemonComponents, meshId: string, nodeId: string, sessionId: string, providerType: string): boolean;
|
|
20
26
|
/**
|
|
21
27
|
* Triggers a queue check for all nodes in the mesh.
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* Safety: mode 0o600, atomic append via appendFileSync
|
|
14
14
|
*/
|
|
15
15
|
import { EventEmitter } from 'events';
|
|
16
|
-
export type MeshLedgerKind = 'task_dispatched' | 'task_completed' | 'task_failed' | 'task_stalled' | 'task_approval_needed' | 'session_launched' | 'session_auto_launch' | 'session_stopped' | 'checkpoint_created' | 'node_cloned' | 'node_joined' | 'node_removed' | 'coordinator_started' | 'recovery_attempted' | 'ledger_replicated' | 'ledger_reconciled' | 'direct_fast_forward';
|
|
16
|
+
export type MeshLedgerKind = 'task_dispatched' | 'task_completed' | 'task_failed' | 'task_stalled' | 'task_approval_needed' | 'p2p_dispatch_failed' | 'session_launched' | 'session_auto_launch' | 'session_stopped' | 'checkpoint_created' | 'node_cloned' | 'node_joined' | 'node_removed' | 'coordinator_started' | 'recovery_attempted' | 'ledger_replicated' | 'ledger_reconciled' | 'direct_fast_forward';
|
|
17
17
|
export interface MeshLedgerEntry {
|
|
18
18
|
id: string;
|
|
19
19
|
meshId: string;
|
|
@@ -151,6 +151,26 @@ export interface AppendRemoteLedgerResult {
|
|
|
151
151
|
}
|
|
152
152
|
export declare const MAX_LEDGER_SLICE_LIMIT = 500;
|
|
153
153
|
export declare function getLedgerDir(): string;
|
|
154
|
+
/**
|
|
155
|
+
* Footer to append to worker task messages so workers output structured results
|
|
156
|
+
* that the daemon parses via extractJsonObjectFromSummary / normalizeMeshWorkerResult.
|
|
157
|
+
*
|
|
158
|
+
* Usage: append buildWorkerTaskFooter() to the task message in mesh_send_task /
|
|
159
|
+
* mesh_enqueue_task. The coordinator prompt rules instruct coordinators to do this.
|
|
160
|
+
*/
|
|
161
|
+
export declare function buildWorkerTaskFooter(): string;
|
|
162
|
+
/**
|
|
163
|
+
* Compact the active ledger file for a mesh by moving old terminal entries
|
|
164
|
+
* (task_completed, task_failed, task_stalled, recovery_attempted older than 7 days)
|
|
165
|
+
* to <meshId>.archive.jsonl, keeping the active file lean.
|
|
166
|
+
*
|
|
167
|
+
* Non-terminal entries (dispatch, sessions, node lifecycle) are always retained.
|
|
168
|
+
* Called automatically from appendLedgerEntry when the file exceeds COMPACT_THRESHOLD_BYTES.
|
|
169
|
+
*/
|
|
170
|
+
export declare function compactLedger(meshId: string): {
|
|
171
|
+
archivedCount: number;
|
|
172
|
+
retainedCount: number;
|
|
173
|
+
};
|
|
154
174
|
export declare function normalizeMeshWorkerResult(input?: Record<string, unknown>, source?: MeshWorkerResultArtifact['source']): MeshWorkerResultArtifact;
|
|
155
175
|
export declare function buildTaskCompletionEvidence(opts: BuildTaskCompletionEvidenceOptions): MeshTaskCompletionEvidence;
|
|
156
176
|
/**
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { MeshLedgerEntry } from './mesh-ledger.js';
|
|
2
2
|
import type { PendingMeshCoordinatorEvent } from './mesh-events.js';
|
|
3
|
+
import type { MeshAsyncJobLifecycle } from '../repo-mesh-types.js';
|
|
3
4
|
export type MeshAsyncRefineJobStatus = 'accepted' | 'running' | 'completed' | 'failed';
|
|
4
|
-
export interface MeshAsyncRefineJobSummary {
|
|
5
|
+
export interface MeshAsyncRefineJobSummary extends MeshAsyncJobLifecycle {
|
|
5
6
|
jobId: string;
|
|
6
7
|
interactionId?: string;
|
|
7
8
|
status: MeshAsyncRefineJobStatus;
|
|
@@ -12,8 +13,6 @@ export interface MeshAsyncRefineJobSummary {
|
|
|
12
13
|
workspace?: string;
|
|
13
14
|
branch?: string;
|
|
14
15
|
into?: string;
|
|
15
|
-
startedAt?: string;
|
|
16
|
-
completedAt?: string;
|
|
17
16
|
retryOfJobId?: string;
|
|
18
17
|
lastEvent?: string;
|
|
19
18
|
lastLedgerKind?: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { RepoMeshDaemonRole } from '../repo-mesh-types.js';
|
|
2
|
+
import { BeadsDB } from './beads-db.js';
|
|
2
3
|
export type MeshTaskStatus = 'pending' | 'assigned' | 'completed' | 'failed' | 'cancelled';
|
|
3
4
|
export type MeshActiveTaskStatus = Extract<MeshTaskStatus, 'pending' | 'assigned'>;
|
|
4
5
|
export type MeshHistoricalTaskStatus = Extract<MeshTaskStatus, 'completed' | 'failed' | 'cancelled'>;
|
|
@@ -127,3 +128,19 @@ export declare function getMeshQueueStats(meshId: string): MeshWorkQueueStats;
|
|
|
127
128
|
export declare function __replaceMeshQueueForTests(meshId: string, queue: MeshWorkQueueEntry[]): void;
|
|
128
129
|
export declare function __clearMeshQueueForTests(meshId: string): void;
|
|
129
130
|
export declare function __resetBeadsDBForTests(): void;
|
|
131
|
+
export type DirectDispatchRecord = ReturnType<BeadsDB['getActiveDirectDispatches']>[number];
|
|
132
|
+
export declare function insertDirectDispatch(meshId: string, data: {
|
|
133
|
+
taskId: string;
|
|
134
|
+
nodeId?: string;
|
|
135
|
+
sessionId?: string;
|
|
136
|
+
providerType?: string;
|
|
137
|
+
message: string;
|
|
138
|
+
taskMode?: string;
|
|
139
|
+
via: string;
|
|
140
|
+
dispatchedToIdleSession?: boolean;
|
|
141
|
+
dispatchedAt: string;
|
|
142
|
+
}): void;
|
|
143
|
+
export declare function getActiveDirectDispatches(meshId: string): DirectDispatchRecord[];
|
|
144
|
+
export declare function updateDirectDispatchStatus(meshId: string, sessionId: string, status: 'acked' | 'completed' | 'failed' | 'stale'): void;
|
|
145
|
+
export declare function cleanupTerminalDirectDispatches(olderThanMs?: number): void;
|
|
146
|
+
export declare function markStaleDirectDispatches(meshId: string, olderThanMs?: number): void;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type MeshRefineValidationCommandPlan, type RepoMeshRefineValidationCommandConfig } from './refine-config.js';
|
|
2
|
+
import type { MeshAsyncJobLifecycle } from '../repo-mesh-types.js';
|
|
2
3
|
export type WorktreeBootstrapStatus = 'ready' | 'running' | 'failed' | 'not_configured' | 'disabled' | 'stale';
|
|
3
4
|
export interface RepoMeshWorktreeBootstrapConfig {
|
|
4
5
|
version: 1;
|
|
@@ -8,16 +9,13 @@ export interface RepoMeshWorktreeBootstrapConfig {
|
|
|
8
9
|
commands?: RepoMeshRefineValidationCommandConfig[];
|
|
9
10
|
staleInputs?: string[];
|
|
10
11
|
}
|
|
11
|
-
export interface WorktreeBootstrapState {
|
|
12
|
+
export interface WorktreeBootstrapState extends MeshAsyncJobLifecycle {
|
|
12
13
|
status: WorktreeBootstrapStatus;
|
|
13
14
|
required: boolean;
|
|
14
15
|
configSource?: string;
|
|
15
16
|
configSourceType?: 'repo_file' | 'mesh_policy' | 'unavailable' | 'invalid';
|
|
16
|
-
startedAt?: string;
|
|
17
|
-
completedAt?: string;
|
|
18
17
|
lastCommand?: string;
|
|
19
18
|
exitCode?: number | null;
|
|
20
|
-
error?: string;
|
|
21
19
|
commandsRun?: Array<Record<string, unknown>>;
|
|
22
20
|
staleInputs?: string[];
|
|
23
21
|
}
|
package/package.json
CHANGED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CliScriptRunner — isolated execution of provider CLI scripts
|
|
3
|
+
*
|
|
4
|
+
* Responsible solely for invoking provider-supplied JavaScript functions
|
|
5
|
+
* (detectStatus, parseApproval, parseSession, etc.) and managing the
|
|
6
|
+
* per-session script state created by createState().
|
|
7
|
+
*
|
|
8
|
+
* The runner is stateless with respect to PTY / buffer content — all
|
|
9
|
+
* input data is passed explicitly by the caller so that the adapter can
|
|
10
|
+
* remain a pure transport layer without embedding parsing logic.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { LOG } from '../logging/logger.js';
|
|
14
|
+
import {
|
|
15
|
+
listCliScriptNames,
|
|
16
|
+
type CliApprovalInput,
|
|
17
|
+
type CliScripts,
|
|
18
|
+
type CliScriptInput,
|
|
19
|
+
type CliScreenSnapshot,
|
|
20
|
+
type CliStatusInput,
|
|
21
|
+
type ParsedSession,
|
|
22
|
+
} from './provider-cli-shared.js';
|
|
23
|
+
|
|
24
|
+
export class CliScriptRunner {
|
|
25
|
+
private scripts: CliScripts = {};
|
|
26
|
+
private scriptState: unknown = null;
|
|
27
|
+
private _parseErrorMessage: string | null = null;
|
|
28
|
+
private readonly cliType: string;
|
|
29
|
+
|
|
30
|
+
constructor(cliType: string) {
|
|
31
|
+
this.cliType = cliType;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// ─── Script lifecycle ─────────────────────────────
|
|
35
|
+
|
|
36
|
+
setScripts(scripts: CliScripts): void {
|
|
37
|
+
this.scripts = scripts;
|
|
38
|
+
this._parseErrorMessage = null;
|
|
39
|
+
this.scriptState = typeof scripts.createState === 'function'
|
|
40
|
+
? (scripts.createState() ?? null)
|
|
41
|
+
: null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Reset per-session state — called when the PTY process exits. */
|
|
45
|
+
resetSessionState(): void {
|
|
46
|
+
this.scriptState = null;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ─── Script access (for reflection and test patching) ────────────────────
|
|
50
|
+
|
|
51
|
+
/** Returns the live scripts object. Direct property assignment on this object
|
|
52
|
+
* patches individual scripts without replacing others (used in tests). */
|
|
53
|
+
get cliScripts(): CliScripts { return this.scripts; }
|
|
54
|
+
|
|
55
|
+
// ─── Capability checks ────────────────────────────
|
|
56
|
+
|
|
57
|
+
hasDetectStatus(): boolean {
|
|
58
|
+
return typeof this.scripts.detectStatus === 'function';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
hasParseSession(): boolean {
|
|
62
|
+
return typeof this.scripts.parseSession === 'function';
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getScriptNames(): string[] {
|
|
66
|
+
return listCliScriptNames(this.scripts);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// ─── Error state ──────────────────────────────────
|
|
70
|
+
|
|
71
|
+
get parseErrorMessage(): string | null {
|
|
72
|
+
return this._parseErrorMessage;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
clearParseError(): void {
|
|
76
|
+
this._parseErrorMessage = null;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// ─── Script invocation ────────────────────────────
|
|
80
|
+
|
|
81
|
+
detectStatus(input: CliStatusInput): string | null {
|
|
82
|
+
if (!this.scripts.detectStatus) return null;
|
|
83
|
+
try {
|
|
84
|
+
return this.invoke<string | null>(this.scripts.detectStatus, input);
|
|
85
|
+
} catch (e: any) {
|
|
86
|
+
LOG.warn('CLI', `[${this.cliType}] detectStatus error: ${e?.message || e}`);
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
parseApproval(
|
|
92
|
+
input: CliApprovalInput,
|
|
93
|
+
): { message: string; buttons: string[] } | null {
|
|
94
|
+
if (!this.scripts.parseApproval) return null;
|
|
95
|
+
try {
|
|
96
|
+
return this.invoke<{ message: string; buttons: string[] } | null>(
|
|
97
|
+
this.scripts.parseApproval,
|
|
98
|
+
input,
|
|
99
|
+
);
|
|
100
|
+
} catch (e: any) {
|
|
101
|
+
LOG.warn('CLI', `[${this.cliType}] parseApproval error: ${e?.message || e}`);
|
|
102
|
+
return null;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
parseSession(
|
|
107
|
+
input: CliScriptInput & { tail?: string; tailScreen?: CliScreenSnapshot },
|
|
108
|
+
): ParsedSession | null {
|
|
109
|
+
if (!this.scripts.parseSession) {
|
|
110
|
+
this._parseErrorMessage = `${this.cliType} parseSession unavailable`;
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
try {
|
|
114
|
+
const result = this.invoke<ParsedSession | null>(this.scripts.parseSession, input);
|
|
115
|
+
this._parseErrorMessage = null;
|
|
116
|
+
return result && typeof result === 'object' ? result : null;
|
|
117
|
+
} catch (e: any) {
|
|
118
|
+
this._parseErrorMessage = e?.message || String(e);
|
|
119
|
+
LOG.warn('CLI', `[${this.cliType}] parseSession error: ${this._parseErrorMessage}`);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Invoke an arbitrary named script (e.g. setModel, openModelPicker).
|
|
126
|
+
* Throws if the script is not available.
|
|
127
|
+
*/
|
|
128
|
+
invokeByName(name: string, input: any): any {
|
|
129
|
+
const fn = this.scripts[name];
|
|
130
|
+
if (typeof fn !== 'function') {
|
|
131
|
+
throw new Error(`CLI script '${name}' not available`);
|
|
132
|
+
}
|
|
133
|
+
return this.invoke(fn, input);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ─── Internal ─────────────────────────────────────
|
|
137
|
+
|
|
138
|
+
private invoke<T>(fn: Function, input: any): T {
|
|
139
|
+
const hasStateFactory = typeof this.scripts.createState === 'function';
|
|
140
|
+
const expectsState = hasStateFactory || this.scriptState !== null || fn.length >= 2;
|
|
141
|
+
return expectsState
|
|
142
|
+
? (fn as (state: unknown, input: any) => T)(this.scriptState, input)
|
|
143
|
+
: (fn as (input: any) => T)(input);
|
|
144
|
+
}
|
|
145
|
+
}
|