@memberjunction/server 5.41.0 → 5.42.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/agentSessions/index.d.ts +1 -0
- package/dist/agentSessions/index.d.ts.map +1 -1
- package/dist/agentSessions/index.js +5 -0
- package/dist/agentSessions/index.js.map +1 -1
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts +102 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.d.ts.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.js +164 -0
- package/dist/agentSessions/remoteBrowserGoalEngine.js.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts +66 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.d.ts.map +1 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.js +86 -0
- package/dist/agentSessions/remoteBrowserGoalRegistry.js.map +1 -0
- package/dist/auth/initializeProviders.d.ts.map +1 -1
- package/dist/auth/initializeProviders.js +4 -7
- package/dist/auth/initializeProviders.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +12 -2
- package/dist/config.js.map +1 -1
- package/dist/generated/generated.d.ts +591 -0
- package/dist/generated/generated.d.ts.map +1 -1
- package/dist/generated/generated.js +6832 -3610
- package/dist/generated/generated.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +33 -16
- package/dist/index.js.map +1 -1
- package/dist/integration/CustomColumnPromoter.d.ts +108 -0
- package/dist/integration/CustomColumnPromoter.d.ts.map +1 -0
- package/dist/integration/CustomColumnPromoter.js +508 -0
- package/dist/integration/CustomColumnPromoter.js.map +1 -0
- package/dist/logging/StartupLogger.d.ts +57 -1
- package/dist/logging/StartupLogger.d.ts.map +1 -1
- package/dist/logging/StartupLogger.js +115 -6
- package/dist/logging/StartupLogger.js.map +1 -1
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts +27 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.d.ts.map +1 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.js +136 -0
- package/dist/resolvers/ExecuteRemoteOperationResolver.js.map +1 -0
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts +101 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.d.ts.map +1 -1
- package/dist/resolvers/IntegrationDiscoveryResolver.js +544 -42
- package/dist/resolvers/IntegrationDiscoveryResolver.js.map +1 -1
- package/dist/resolvers/RealtimeBridgeResolver.d.ts +121 -0
- package/dist/resolvers/RealtimeBridgeResolver.d.ts.map +1 -0
- package/dist/resolvers/RealtimeBridgeResolver.js +533 -0
- package/dist/resolvers/RealtimeBridgeResolver.js.map +1 -0
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts +78 -5
- package/dist/resolvers/RemoteBrowserActionResolver.d.ts.map +1 -1
- package/dist/resolvers/RemoteBrowserActionResolver.js +227 -15
- package/dist/resolvers/RemoteBrowserActionResolver.js.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.d.ts.map +1 -1
- package/dist/resolvers/RunAIAgentResolver.js +0 -7
- package/dist/resolvers/RunAIAgentResolver.js.map +1 -1
- package/dist/rest/setupRESTEndpoints.d.ts.map +1 -1
- package/dist/rest/setupRESTEndpoints.js +5 -3
- package/dist/rest/setupRESTEndpoints.js.map +1 -1
- package/package.json +83 -78
- package/src/__tests__/RealtimeBridgeResolver.test.ts +100 -0
- package/src/__tests__/RemoteBrowserAudioStream.test.ts +8 -1
- package/src/__tests__/RemoteBrowserGoalEngine.test.ts +144 -0
- package/src/__tests__/remoteBrowserGoalRegistry.test.ts +81 -0
- package/src/agentSessions/index.ts +5 -0
- package/src/agentSessions/remoteBrowserGoalEngine.ts +191 -0
- package/src/agentSessions/remoteBrowserGoalRegistry.ts +122 -0
- package/src/auth/initializeProviders.ts +4 -7
- package/src/config.ts +13 -2
- package/src/generated/generated.ts +2444 -205
- package/src/index.ts +33 -16
- package/src/integration/CustomColumnPromoter.ts +597 -0
- package/src/logging/StartupLogger.ts +130 -6
- package/src/resolvers/ExecuteRemoteOperationResolver.ts +108 -0
- package/src/resolvers/IntegrationDiscoveryResolver.ts +396 -37
- package/src/resolvers/RealtimeBridgeResolver.ts +450 -0
- package/src/resolvers/RemoteBrowserActionResolver.ts +232 -17
- package/src/resolvers/RunAIAgentResolver.ts +1 -9
- package/src/rest/setupRESTEndpoints.ts +5 -3
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { BaseSingleton } from '@memberjunction/global';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Terminal/structural outcome of a remote-browser goal run — the fields the client needs to render
|
|
5
|
+
* the result, decoupled from the GraphQL `RemoteBrowserGoalResultType` (avoids a resolver↔registry
|
|
6
|
+
* import cycle) and from the engine's `RemoteBrowserGoalResult` (whose `Strategy` is a string union).
|
|
7
|
+
*/
|
|
8
|
+
export interface RemoteBrowserGoalOutcome {
|
|
9
|
+
Success: boolean;
|
|
10
|
+
Strategy?: string;
|
|
11
|
+
CurrentUrl?: string;
|
|
12
|
+
Status?: string;
|
|
13
|
+
StepCount?: number;
|
|
14
|
+
Detail?: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
type GoalRunStatus = 'Running' | 'Complete';
|
|
18
|
+
|
|
19
|
+
/** One tracked goal run: its lifecycle status plus the terminal outcome once it finishes. */
|
|
20
|
+
export interface GoalRunRecord {
|
|
21
|
+
GoalRunID: string;
|
|
22
|
+
/** Lowercased `AIAgentSession` id the goal runs against (one in-flight goal per session). */
|
|
23
|
+
AgentSessionID: string;
|
|
24
|
+
Status: GoalRunStatus;
|
|
25
|
+
Outcome?: RemoteBrowserGoalOutcome;
|
|
26
|
+
StartedAt: number;
|
|
27
|
+
CompletedAt?: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/** Default time a COMPLETED record is retained so a slow poller can still read it (then swept). */
|
|
31
|
+
const COMPLETED_TTL_MS = 5 * 60 * 1000;
|
|
32
|
+
/** Safety cap: a record that never completes (e.g. a crashed loop) is swept after this long. */
|
|
33
|
+
const RUNNING_MAX_MS = 30 * 60 * 1000;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Process-local registry of in-flight + recently-completed remote-browser GOAL runs.
|
|
37
|
+
*
|
|
38
|
+
* ## Why this exists
|
|
39
|
+
* A goal-driven browser run (computer-use loop) can take many minutes. Running it inside a single
|
|
40
|
+
* synchronous GraphQL mutation means the client holds one long-lived HTTP request open for the whole
|
|
41
|
+
* loop — fragile against browser fetch timeouts, proxy/ngrok idle limits, and session-janitor churn.
|
|
42
|
+
* Observed symptom: the loop completes successfully server-side but the client's request has already
|
|
43
|
+
* died, so the agent gets "no response from the server" and never learns the outcome.
|
|
44
|
+
*
|
|
45
|
+
* The fix is async: the start mutation kicks the goal off and registers it here (status `Running`),
|
|
46
|
+
* returning a `GoalRunID` immediately; the client then POLLS a short query that reads this registry
|
|
47
|
+
* until the run is `Complete`. Every request stays short, so no transport boundary times out.
|
|
48
|
+
*
|
|
49
|
+
* Keyed by `AIAgentSession` id (at most one in-flight goal per session, mirroring the engine's
|
|
50
|
+
* per-session abort registry). Process-local is correct: the browser session — and thus the goal —
|
|
51
|
+
* is pinned to the MJAPI instance hosting its CDP connection, the same instance the client's
|
|
52
|
+
* session-scoped mutations already target.
|
|
53
|
+
*/
|
|
54
|
+
export class RemoteBrowserGoalRegistry extends BaseSingleton<RemoteBrowserGoalRegistry> {
|
|
55
|
+
private runs = new Map<string, GoalRunRecord>();
|
|
56
|
+
|
|
57
|
+
protected constructor() {
|
|
58
|
+
super();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public static get Instance(): RemoteBrowserGoalRegistry {
|
|
62
|
+
return super.getInstance<RemoteBrowserGoalRegistry>();
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
private key(agentSessionID: string): string {
|
|
66
|
+
return (agentSessionID ?? '').toLowerCase();
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Registers a newly-started goal as `Running`, replacing any prior record for the session. */
|
|
70
|
+
public Begin(agentSessionID: string, goalRunID: string): void {
|
|
71
|
+
this.sweep();
|
|
72
|
+
this.runs.set(this.key(agentSessionID), {
|
|
73
|
+
GoalRunID: goalRunID,
|
|
74
|
+
AgentSessionID: this.key(agentSessionID),
|
|
75
|
+
Status: 'Running',
|
|
76
|
+
StartedAt: Date.now(),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Marks a goal run `Complete` with its terminal outcome. No-op when the session's current record is
|
|
82
|
+
* for a DIFFERENT `goalRunID` (a newer goal superseded it) — the stale completion is dropped.
|
|
83
|
+
*/
|
|
84
|
+
public Complete(agentSessionID: string, goalRunID: string, outcome: RemoteBrowserGoalOutcome): void {
|
|
85
|
+
const rec = this.runs.get(this.key(agentSessionID));
|
|
86
|
+
if (!rec || rec.GoalRunID !== goalRunID) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
rec.Status = 'Complete';
|
|
90
|
+
rec.Outcome = outcome;
|
|
91
|
+
rec.CompletedAt = Date.now();
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns the tracked record for a session, or `undefined` when none exists or `goalRunID` is
|
|
96
|
+
* supplied and doesn't match the current run (so a poll for an expired/superseded id reads nothing).
|
|
97
|
+
*/
|
|
98
|
+
public Get(agentSessionID: string, goalRunID?: string): GoalRunRecord | undefined {
|
|
99
|
+
this.sweep();
|
|
100
|
+
const rec = this.runs.get(this.key(agentSessionID));
|
|
101
|
+
if (!rec) {
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
if (goalRunID && rec.GoalRunID !== goalRunID) {
|
|
105
|
+
return undefined;
|
|
106
|
+
}
|
|
107
|
+
return rec;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/** Drops completed records past their TTL and never-completing records past the safety cap. */
|
|
111
|
+
private sweep(): void {
|
|
112
|
+
const now = Date.now();
|
|
113
|
+
for (const [k, rec] of this.runs) {
|
|
114
|
+
const expired =
|
|
115
|
+
(rec.Status === 'Complete' && rec.CompletedAt != null && now - rec.CompletedAt > COMPLETED_TTL_MS) ||
|
|
116
|
+
now - rec.StartedAt > RUNNING_MAX_MS;
|
|
117
|
+
if (expired) {
|
|
118
|
+
this.runs.delete(k);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { configInfo } from '../config.js';
|
|
2
|
-
import { AuthProviderConfig, LogError
|
|
2
|
+
import { AuthProviderConfig, LogError } from '@memberjunction/core';
|
|
3
3
|
import { AuthProviderFactory } from '@memberjunction/auth-providers';
|
|
4
|
-
import { StartupLogger } from '../logging/StartupLogger.js';
|
|
5
4
|
|
|
6
5
|
/**
|
|
7
6
|
* Initialize authentication providers from configuration
|
|
@@ -17,12 +16,10 @@ export function initializeAuthProviders(): void {
|
|
|
17
16
|
for (const providerConfig of configInfo.authProviders) {
|
|
18
17
|
try {
|
|
19
18
|
const provider = AuthProviderFactory.createProvider(providerConfig as AuthProviderConfig);
|
|
19
|
+
// register() emits the verbose-only "Registered auth provider: … with issuer: …" line
|
|
20
|
+
// (gated by the global verbose flag). Provider NAMES are surfaced compactly in the
|
|
21
|
+
// startup summary `Auth` line at `standard`.
|
|
20
22
|
factory.register(provider);
|
|
21
|
-
// Per-provider registration detail — verbose-only. The provider NAMES are
|
|
22
|
-
// surfaced compactly in the startup summary `Auth` line at `standard`.
|
|
23
|
-
if (new StartupLogger().IsAtLeast('verbose')) {
|
|
24
|
-
LogStatus(`Registered auth provider: ${provider.name} (type: ${providerConfig.type})`);
|
|
25
|
-
}
|
|
26
23
|
} catch (error) {
|
|
27
24
|
LogError(`Failed to initialize auth provider ${providerConfig.name}: ${error}`);
|
|
28
25
|
}
|
package/src/config.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { cosmiconfigSync } from 'cosmiconfig';
|
|
3
|
-
import { LogError, LogStatus } from '@memberjunction/core';
|
|
3
|
+
import { LogError, LogStatus, LogStatusEx } from '@memberjunction/core';
|
|
4
4
|
import { mergeConfigs, parseBooleanEnv } from '@memberjunction/config';
|
|
5
5
|
|
|
6
6
|
const explorer = cosmiconfigSync('mj', { searchStrategy: 'global' });
|
|
@@ -586,6 +586,14 @@ export const DEFAULT_SERVER_CONFIG: Partial<ConfigInfo> = {
|
|
|
586
586
|
].filter(Boolean),
|
|
587
587
|
};
|
|
588
588
|
|
|
589
|
+
/**
|
|
590
|
+
* Absolute path to the resolved config file, captured during {@link loadConfig}.
|
|
591
|
+
* `undefined` when no config file was found (defaults in use). Rendered in the
|
|
592
|
+
* startup summary `Config` line. Declared before `configInfo` so the assignment
|
|
593
|
+
* inside `loadConfig()` (invoked below) is not in its temporal dead zone.
|
|
594
|
+
*/
|
|
595
|
+
export let configFilePath: string | undefined;
|
|
596
|
+
|
|
589
597
|
export const configInfo: ConfigInfo = loadConfig();
|
|
590
598
|
|
|
591
599
|
export const {
|
|
@@ -621,7 +629,10 @@ export function loadConfig() {
|
|
|
621
629
|
|
|
622
630
|
// If user config exists, merge it with defaults
|
|
623
631
|
if (configSearchResult && !configSearchResult.isEmpty) {
|
|
624
|
-
|
|
632
|
+
// Resolved config-file path. Surfaced in the startup summary `Config` line at standard
|
|
633
|
+
// level (see StartupLogger). Demoted to verbose-only here to avoid a duplicate inline line.
|
|
634
|
+
configFilePath = configSearchResult.filepath;
|
|
635
|
+
LogStatusEx({ message: `Config file found at ${configSearchResult.filepath}`, verboseOnly: true });
|
|
625
636
|
|
|
626
637
|
// Merge user config with defaults (user config takes precedence)
|
|
627
638
|
mergedConfig = mergeConfigs(DEFAULT_SERVER_CONFIG, configSearchResult.config);
|