@undefineds.co/linx 0.3.4 → 0.3.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/README.md +58 -23
- package/dist/generated/version.js +1 -1
- package/dist/generated/version.js.map +1 -1
- package/dist/index.js +334 -162
- package/dist/index.js.map +1 -1
- package/dist/lib/account-session.js +4 -8
- package/dist/lib/account-session.js.map +1 -1
- package/dist/lib/ai-command.js +228 -178
- package/dist/lib/ai-command.js.map +1 -1
- package/dist/lib/auto-mode/archive.js +38 -7
- package/dist/lib/auto-mode/archive.js.map +1 -1
- package/dist/lib/auto-mode/auth.js.map +1 -1
- package/dist/lib/auto-mode/display.js +71 -45
- package/dist/lib/auto-mode/display.js.map +1 -1
- package/dist/lib/auto-mode/format.js +9 -7
- package/dist/lib/auto-mode/format.js.map +1 -1
- package/dist/lib/auto-mode/hooks/claude.js +12 -2
- package/dist/lib/auto-mode/hooks/claude.js.map +1 -1
- package/dist/lib/auto-mode/hooks/codex.js +17 -7
- package/dist/lib/auto-mode/hooks/codex.js.map +1 -1
- package/dist/lib/auto-mode/hooks/index.js +28 -8
- package/dist/lib/auto-mode/hooks/index.js.map +1 -1
- package/dist/lib/auto-mode/pod-ai.js +20 -37
- package/dist/lib/auto-mode/pod-ai.js.map +1 -1
- package/dist/lib/auto-mode/pod-approval.js +124 -195
- package/dist/lib/auto-mode/pod-approval.js.map +1 -1
- package/dist/lib/auto-mode/pod-persistence.js +169 -90
- package/dist/lib/auto-mode/pod-persistence.js.map +1 -1
- package/dist/lib/auto-mode/runner.js +683 -81
- package/dist/lib/auto-mode/runner.js.map +1 -1
- package/dist/lib/auto-mode/secretary.js +186 -41
- package/dist/lib/auto-mode/secretary.js.map +1 -1
- package/dist/lib/auto-mode-command.js +32 -32
- package/dist/lib/auto-mode-command.js.map +1 -1
- package/dist/lib/chat-api.js +242 -50
- package/dist/lib/chat-api.js.map +1 -1
- package/dist/lib/codex-plugin/bridge.js +164 -17
- package/dist/lib/codex-plugin/bridge.js.map +1 -1
- package/dist/lib/codex-plugin/codex-native-proxy.js +370 -34
- package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
- package/dist/lib/credentials-store.js +33 -42
- package/dist/lib/credentials-store.js.map +1 -1
- package/dist/lib/linx-cloud-errors.js +61 -0
- package/dist/lib/linx-cloud-errors.js.map +1 -0
- package/dist/lib/linx-tui-contract.js +8 -5
- package/dist/lib/linx-tui-contract.js.map +1 -1
- package/dist/lib/login-command.js +9 -2
- package/dist/lib/login-command.js.map +1 -1
- package/dist/lib/models.js +3 -20
- package/dist/lib/models.js.map +1 -1
- package/dist/lib/oidc-auth.js +143 -17
- package/dist/lib/oidc-auth.js.map +1 -1
- package/dist/lib/oidc-session-storage.js +2 -6
- package/dist/lib/oidc-session-storage.js.map +1 -1
- package/dist/lib/pi-adapter/auto-input-controller.js +988 -0
- package/dist/lib/pi-adapter/auto-input-controller.js.map +1 -0
- package/dist/lib/pi-adapter/backend-command.js +2 -0
- package/dist/lib/pi-adapter/backend-command.js.map +1 -0
- package/dist/lib/pi-adapter/backend-credentials.js +80 -0
- package/dist/lib/pi-adapter/backend-credentials.js.map +1 -0
- package/dist/lib/pi-adapter/branding.js +246 -108
- package/dist/lib/pi-adapter/branding.js.map +1 -1
- package/dist/lib/pi-adapter/control-state.js +72 -0
- package/dist/lib/pi-adapter/control-state.js.map +1 -0
- package/dist/lib/pi-adapter/interactive.js +2634 -30
- package/dist/lib/pi-adapter/interactive.js.map +1 -1
- package/dist/lib/pi-adapter/pod-approval.js +382 -210
- package/dist/lib/pi-adapter/pod-approval.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror-mapping.js +71 -17
- package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +1 -1
- package/dist/lib/pi-adapter/pod-mirror.js +531 -64
- package/dist/lib/pi-adapter/pod-mirror.js.map +1 -1
- package/dist/lib/pi-adapter/pod-native.js +81 -85
- package/dist/lib/pi-adapter/pod-native.js.map +1 -1
- package/dist/lib/pi-adapter/pod-status-output.js +54 -0
- package/dist/lib/pi-adapter/pod-status-output.js.map +1 -0
- package/dist/lib/pi-adapter/runtime.js +458 -228
- package/dist/lib/pi-adapter/runtime.js.map +1 -1
- package/dist/lib/pi-adapter/session-control.js +509 -0
- package/dist/lib/pi-adapter/session-control.js.map +1 -0
- package/dist/lib/pi-adapter/session.js +35 -22
- package/dist/lib/pi-adapter/session.js.map +1 -1
- package/dist/lib/pi-adapter/stream.js +89 -32
- package/dist/lib/pi-adapter/stream.js.map +1 -1
- package/dist/lib/pi-adapter/sync-recovery.js +89 -0
- package/dist/lib/pi-adapter/sync-recovery.js.map +1 -0
- package/dist/lib/pi-adapter/web-fetch.js +13 -14
- package/dist/lib/pi-adapter/web-fetch.js.map +1 -1
- package/dist/lib/pod-chat-store.js +254 -78
- package/dist/lib/pod-chat-store.js.map +1 -1
- package/dist/lib/pod-data-session.js +156 -35
- package/dist/lib/pod-data-session.js.map +1 -1
- package/dist/lib/solid-auth-store.js +27 -0
- package/dist/lib/solid-auth-store.js.map +1 -0
- package/dist/lib/solid-auth.js +2 -4
- package/dist/lib/solid-auth.js.map +1 -1
- package/dist/lib/solid-client-credentials-login.js +100 -0
- package/dist/lib/solid-client-credentials-login.js.map +1 -0
- package/dist/lib/solid-local-store.js +31 -0
- package/dist/lib/solid-local-store.js.map +1 -0
- package/dist/lib/symphony/archive.js +328 -18
- package/dist/lib/symphony/archive.js.map +1 -1
- package/dist/lib/symphony/pod-projection.js +2222 -0
- package/dist/lib/symphony/pod-projection.js.map +1 -0
- package/dist/lib/symphony-command.js +602 -178
- package/dist/lib/symphony-command.js.map +1 -1
- package/dist/lib/sync-checkpoint-store.js +74 -0
- package/dist/lib/sync-checkpoint-store.js.map +1 -0
- package/dist/skills/symphony/SKILL.md +665 -0
- package/package.json +15 -9
- package/vendor/agent-runtime/dist/agent-runtime.d.ts +137 -0
- package/vendor/agent-runtime/dist/agent-runtime.js +211 -0
- package/vendor/agent-runtime/dist/auto-mode.d.ts +78 -13
- package/vendor/agent-runtime/dist/auto-mode.js +288 -31
- package/vendor/agent-runtime/dist/control-plane.d.ts +28 -0
- package/vendor/agent-runtime/dist/control-plane.js +79 -0
- package/vendor/agent-runtime/dist/file-sync.d.ts +157 -0
- package/vendor/agent-runtime/dist/file-sync.js +314 -0
- package/vendor/agent-runtime/dist/index.d.ts +7 -0
- package/vendor/agent-runtime/dist/index.js +7 -0
- package/vendor/agent-runtime/dist/reconciler.d.ts +117 -0
- package/vendor/agent-runtime/dist/reconciler.js +361 -0
- package/vendor/agent-runtime/dist/symphony.d.ts +128 -8
- package/vendor/agent-runtime/dist/symphony.js +362 -57
- package/vendor/agent-runtime/dist/sync.d.ts +271 -0
- package/vendor/agent-runtime/dist/sync.js +550 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.d.ts +58 -0
- package/vendor/agent-runtime/dist/thread-reconciler-controller.js +137 -0
- package/vendor/agent-runtime/dist/turn-controller.js +2 -2
- package/vendor/agent-runtime/dist/wake-scheduler.d.ts +67 -0
- package/vendor/agent-runtime/dist/wake-scheduler.js +194 -0
- package/vendor/agent-runtime/package.json +8 -1
- package/vendor/pi-web-access/CHANGELOG.md +387 -0
- package/vendor/pi-web-access/LICENSE +21 -0
- package/vendor/pi-web-access/README.md +352 -0
- package/vendor/pi-web-access/activity.ts +101 -0
- package/vendor/pi-web-access/banner.png +0 -0
- package/vendor/pi-web-access/chrome-cookies.ts +322 -0
- package/vendor/pi-web-access/code-search.ts +107 -0
- package/vendor/pi-web-access/curator-page.ts +3359 -0
- package/vendor/pi-web-access/curator-server.ts +605 -0
- package/vendor/pi-web-access/exa.ts +520 -0
- package/vendor/pi-web-access/extract.ts +641 -0
- package/vendor/pi-web-access/gemini-api.ts +112 -0
- package/vendor/pi-web-access/gemini-search.ts +361 -0
- package/vendor/pi-web-access/gemini-url-context.ts +126 -0
- package/vendor/pi-web-access/gemini-web-config.ts +52 -0
- package/vendor/pi-web-access/gemini-web.ts +396 -0
- package/vendor/pi-web-access/github-api.ts +196 -0
- package/vendor/pi-web-access/github-extract.ts +634 -0
- package/vendor/pi-web-access/index.ts +2346 -0
- package/vendor/pi-web-access/package.json +45 -0
- package/vendor/pi-web-access/pdf-extract.ts +192 -0
- package/vendor/pi-web-access/perplexity.ts +195 -0
- package/vendor/pi-web-access/pi-web-fetch-demo.mp4 +0 -0
- package/vendor/pi-web-access/rsc-extract.ts +338 -0
- package/vendor/pi-web-access/skills/librarian/SKILL.md +195 -0
- package/vendor/pi-web-access/storage.ts +72 -0
- package/vendor/pi-web-access/summary-review.ts +276 -0
- package/vendor/pi-web-access/test/gemini-web-cookie-opt-in.test.mjs +41 -0
- package/vendor/pi-web-access/test/pdf-extract.test.mjs +95 -0
- package/vendor/pi-web-access/utils.ts +44 -0
- package/vendor/pi-web-access/video-extract.ts +378 -0
- package/vendor/pi-web-access/youtube-extract.ts +310 -0
- package/dist/lib/pi-adapter/auth.js +0 -68
- package/dist/lib/pi-adapter/auth.js.map +0 -1
- package/dist/lib/pi-adapter/pod-tools.js +0 -140
- package/dist/lib/pi-adapter/pod-tools.js.map +0 -1
- package/dist/skills/drizzle-solid/SKILL.md +0 -340
- package/dist/skills/pod-storage/SKILL.md +0 -100
- package/dist/skills/solid-modeling/SKILL.md +0 -274
- package/dist/skills/xpod-componentsjs/SKILL.md +0 -284
|
@@ -1,23 +1,32 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { ensureBrowserConsentLogin, isOidcLoginExpiredError } from '../oidc-auth.js';
|
|
1
|
+
import { createLinxAgentStreamAdapter } from './stream.js';
|
|
2
|
+
import { ensureBrowserConsentLogin, isOidcLoginExpiredError, isOidcTransientRemoteError } from '../oidc-auth.js';
|
|
4
3
|
import { DEFAULT_LINX_CLOUD_MODEL_ID, FALLBACK_LINX_CLOUD_MODEL_IDS, resolvePreferredLinxCloudModelId } from '../default-model.js';
|
|
5
4
|
import { ensureLinxPiTheme } from './theme.js';
|
|
6
|
-
import { AuthStorage, ModelRegistry, SettingsManager, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices,
|
|
7
|
-
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
5
|
+
import { AuthStorage, ModelRegistry, SettingsManager, createAgentSessionFromServices, createAgentSessionRuntime, createAgentSessionServices, createCodingTools, createLocalBashOperations, } from '@earendil-works/pi-coding-agent';
|
|
6
|
+
// web_fetch / web_search are now handled by pi-web-access
|
|
7
|
+
import { existsSync, mkdirSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
8
|
+
import { homedir } from 'node:os';
|
|
9
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
10
|
+
import { createRequire } from 'node:module';
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
|
-
import { isRemoteAuthExpiredError } from '../chat-api.js';
|
|
13
|
-
import {
|
|
12
|
+
import { RemoteChatRequestError, isRemoteAuthExpiredError } from '../chat-api.js';
|
|
13
|
+
import { formatLinxCloudTransientMessage } from '../linx-cloud-errors.js';
|
|
14
|
+
import { clearDefaultPodDataSession, getDefaultPodDataSession } from '../pod-data-session.js';
|
|
15
|
+
import { loadCredentials } from '../credentials-store.js';
|
|
16
|
+
import { getSolidLinxAppDir, getSolidLinxPiWebAccessConfigPath } from '../solid-local-store.js';
|
|
14
17
|
const UNDEFINEDS_PROVIDER_ID = 'undefineds';
|
|
15
|
-
const UNDEFINEDS_PROVIDER_LABEL = '
|
|
16
|
-
const UNDEFINEDS_PROVIDER_API = '
|
|
18
|
+
const UNDEFINEDS_PROVIDER_LABEL = 'LinX Cloud';
|
|
19
|
+
const UNDEFINEDS_PROVIDER_API = 'linx-cloud-chat-completions';
|
|
17
20
|
const UNDEFINEDS_SESSION_ID = 'undefineds_pi_frontend';
|
|
18
21
|
const UNDEFINEDS_AUTH_BRIDGE_ID = 'undefineds-cloud-oauth-bridge';
|
|
22
|
+
export const LINX_RUNTIME_MANAGED_AUTH_KEY = 'linx-runtime-managed-auth';
|
|
19
23
|
const LINX_PACKAGE_SOURCE = '@undefineds.co/linx';
|
|
24
|
+
const LINX_WEB_ACCESS_PACKAGE_SOURCE = 'pi-web-access';
|
|
25
|
+
const LINX_PRODUCT_SKILL_NAMES = new Set(['symphony']);
|
|
26
|
+
const MARKET_XPOD_CLI_SKILL_SOURCE = 'xpod-cli@undefineds';
|
|
20
27
|
export const DEFAULT_LINX_PI_BASH_TIMEOUT_SECONDS = 15;
|
|
28
|
+
const DEFAULT_LINX_CLOUD_CONTEXT_WINDOW = 1_000_000;
|
|
29
|
+
const DEFAULT_LINX_CLOUD_COMPLETION_TIMEOUT_MS = 10 * 60 * 1000;
|
|
21
30
|
export async function resolveLinxStartupLoginPromptDecision(options) {
|
|
22
31
|
if (options.print) {
|
|
23
32
|
return { shouldPrompt: false, reason: 'print-mode' };
|
|
@@ -25,10 +34,16 @@ export async function resolveLinxStartupLoginPromptDecision(options) {
|
|
|
25
34
|
if (options.backend === 'native') {
|
|
26
35
|
return { shouldPrompt: false, reason: 'native-backend' };
|
|
27
36
|
}
|
|
28
|
-
|
|
37
|
+
if (!options.resolveSession) {
|
|
38
|
+
return (options.loadStoredCredentials ?? loadCredentials)()
|
|
39
|
+
? { shouldPrompt: false, reason: 'credential-present' }
|
|
40
|
+
: { shouldPrompt: true, reason: 'missing-credential' };
|
|
41
|
+
}
|
|
42
|
+
const resolveSession = options.resolveSession;
|
|
43
|
+
let session = null;
|
|
29
44
|
try {
|
|
30
|
-
|
|
31
|
-
return
|
|
45
|
+
session = await resolveSession();
|
|
46
|
+
return session
|
|
32
47
|
? { shouldPrompt: false, reason: 'credential-present' }
|
|
33
48
|
: { shouldPrompt: true, reason: 'missing-credential' };
|
|
34
49
|
}
|
|
@@ -36,8 +51,14 @@ export async function resolveLinxStartupLoginPromptDecision(options) {
|
|
|
36
51
|
if (isOidcLoginExpiredError(error)) {
|
|
37
52
|
return { shouldPrompt: true, reason: 'expired-credential' };
|
|
38
53
|
}
|
|
54
|
+
if (isOidcTransientRemoteError(error) && (options.loadStoredCredentials ?? loadCredentials)()) {
|
|
55
|
+
return { shouldPrompt: false, reason: 'credential-present' };
|
|
56
|
+
}
|
|
39
57
|
throw error;
|
|
40
58
|
}
|
|
59
|
+
finally {
|
|
60
|
+
await session?.close().catch(() => undefined);
|
|
61
|
+
}
|
|
41
62
|
}
|
|
42
63
|
export function resolveLinxStartupLoginReason(decision) {
|
|
43
64
|
if (!decision.shouldPrompt) {
|
|
@@ -51,8 +72,9 @@ export function resolveLinxInteractiveLoginReason(options) {
|
|
|
51
72
|
}
|
|
52
73
|
return resolveLinxStartupLoginReason(options.startupDecision);
|
|
53
74
|
}
|
|
54
|
-
export function
|
|
75
|
+
export function createLinxRuntimeAdapter(dependencies, options = {}) {
|
|
55
76
|
const backendMode = options.backend ?? 'cloud';
|
|
77
|
+
const workerBackend = options.workerBackend ?? (backendMode === 'native' ? 'codex' : undefined);
|
|
56
78
|
const cwd = options.cwd ?? process.cwd();
|
|
57
79
|
const requestedModel = options.model?.trim() || undefined;
|
|
58
80
|
let activeModelId = requestedModel ?? DEFAULT_LINX_CLOUD_MODEL_ID;
|
|
@@ -62,17 +84,22 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
62
84
|
const proxy = backendMode === 'native'
|
|
63
85
|
? dependencies.createNativeProxy?.({
|
|
64
86
|
cwd,
|
|
65
|
-
model:
|
|
87
|
+
model: requestedModel,
|
|
66
88
|
listenPort: options.port,
|
|
89
|
+
autoEnabled: options.autoEnabled,
|
|
90
|
+
codexApprovalPolicy: options.codexApprovalPolicy,
|
|
91
|
+
passthroughArgs: options.passthroughArgs,
|
|
92
|
+
env: options.backendEnv,
|
|
93
|
+
resolveEnv: options.resolveBackendEnv,
|
|
67
94
|
})
|
|
68
95
|
: null;
|
|
69
96
|
if (backendMode === 'native' && !proxy) {
|
|
70
|
-
throw new Error('Native
|
|
97
|
+
throw new Error('Native LinX runtime backend requires createNativeProxy');
|
|
71
98
|
}
|
|
72
99
|
if (backendMode === 'cloud' && !options.providerConfig?.oauth && !dependencies.createRemoteCompletion) {
|
|
73
|
-
throw new Error('Cloud
|
|
100
|
+
throw new Error('Cloud LinX runtime backend requires createRemoteCompletion');
|
|
74
101
|
}
|
|
75
|
-
const streamAdapter =
|
|
102
|
+
const streamAdapter = createLinxAgentStreamAdapter({
|
|
76
103
|
sessionId: proxy?.record.id ?? UNDEFINEDS_SESSION_ID,
|
|
77
104
|
cwd: proxy?.record.cwd ?? cwd,
|
|
78
105
|
model: proxy?.record.model ?? activeModelId,
|
|
@@ -89,30 +116,55 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
89
116
|
completionBackend: !proxy && dependencies.createRemoteCompletion
|
|
90
117
|
? {
|
|
91
118
|
async complete(input) {
|
|
92
|
-
const
|
|
93
|
-
? input.
|
|
94
|
-
|
|
119
|
+
const authFetch = options.providerConfig?.oauth
|
|
120
|
+
? input.authFetch
|
|
121
|
+
?? resolveRuntimeAuthFetchFromApiKey(input.apiKey)
|
|
122
|
+
?? await resolveLinxPiCloudAuthFetch({
|
|
123
|
+
issuerUrl: options.providerConfig?.issuerUrl,
|
|
124
|
+
getPodDataSession: options.getPodDataSession,
|
|
125
|
+
})
|
|
126
|
+
: await resolveLinxPiCloudAuthFetch({
|
|
95
127
|
issuerUrl: options.providerConfig?.issuerUrl,
|
|
96
|
-
|
|
128
|
+
getPodDataSession: options.getPodDataSession,
|
|
97
129
|
});
|
|
98
|
-
return completeWithAuthRecovery(
|
|
130
|
+
return completeWithAuthRecovery(authFetch, {
|
|
99
131
|
runtimeUrl: baseUrl,
|
|
100
132
|
model: input.model,
|
|
101
133
|
messages: withSystemPrompt(input.systemPrompt, input.messages),
|
|
102
134
|
tools: input.tools,
|
|
103
|
-
reasoning: input.reasoning,
|
|
104
135
|
signal: input.signal,
|
|
105
136
|
});
|
|
106
137
|
},
|
|
107
138
|
}
|
|
108
139
|
: undefined,
|
|
109
140
|
});
|
|
141
|
+
const backendCommandRouter = proxy && typeof proxy.executeCommand === 'function'
|
|
142
|
+
? {
|
|
143
|
+
backend: proxy.record.backend,
|
|
144
|
+
execute(input) {
|
|
145
|
+
return proxy.executeCommand(input);
|
|
146
|
+
},
|
|
147
|
+
setCwd: typeof proxy.setCwd === 'function'
|
|
148
|
+
? (nextCwd) => proxy.setCwd(nextCwd)
|
|
149
|
+
: undefined,
|
|
150
|
+
subscribe(listener) {
|
|
151
|
+
return proxy.subscribe(listener);
|
|
152
|
+
},
|
|
153
|
+
setSessionControl: typeof proxy.setSessionControl === 'function'
|
|
154
|
+
? (control) => proxy.setSessionControl(control)
|
|
155
|
+
: undefined,
|
|
156
|
+
}
|
|
157
|
+
: undefined;
|
|
110
158
|
return {
|
|
111
159
|
remoteUrl: proxy?.remoteUrl ?? baseUrl,
|
|
112
160
|
sessionId: proxy?.record.id ?? UNDEFINEDS_SESSION_ID,
|
|
113
161
|
cwd: proxy?.record.cwd ?? cwd,
|
|
114
162
|
model: proxy?.record.model ?? activeModelId,
|
|
115
|
-
backend:
|
|
163
|
+
backend: 'linx',
|
|
164
|
+
runtimeBackend: workerBackend,
|
|
165
|
+
autoEnabled: options.autoEnabled === true,
|
|
166
|
+
symphonyEnabled: options.symphonyEnabled === true,
|
|
167
|
+
backendCommandRouter,
|
|
116
168
|
streamAdapter,
|
|
117
169
|
createRuntime: async (context) => {
|
|
118
170
|
const authStorage = AuthStorage.inMemory();
|
|
@@ -136,74 +188,86 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
136
188
|
},
|
|
137
189
|
manualRedirectUrl: callbacks.onManualCodeInput,
|
|
138
190
|
});
|
|
191
|
+
clearDefaultPodDataSession();
|
|
139
192
|
if (result.reusedExistingSession) {
|
|
140
193
|
callbacks.onProgress?.('Reused existing LinX Cloud session.');
|
|
141
194
|
}
|
|
142
|
-
|
|
195
|
+
const authFetch = await resolveLinxPiCloudAuthFetch({
|
|
196
|
+
issuerUrl: options.providerConfig?.issuerUrl,
|
|
197
|
+
getPodDataSession: options.getPodDataSession,
|
|
198
|
+
});
|
|
199
|
+
await syncProviderModels({ runtimeFetch: authFetch });
|
|
143
200
|
return {
|
|
144
201
|
refresh: result.tokenSet.refreshToken ?? '',
|
|
145
|
-
access:
|
|
202
|
+
access: LINX_RUNTIME_MANAGED_AUTH_KEY,
|
|
146
203
|
expires: result.tokenSet.expiresAt ? result.tokenSet.expiresAt * 1000 : Date.now() + 60 * 60 * 1000,
|
|
147
204
|
};
|
|
148
205
|
},
|
|
149
|
-
async refreshToken() {
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
206
|
+
async refreshToken(credentials) {
|
|
207
|
+
clearDefaultPodDataSession();
|
|
208
|
+
const authFetch = await resolveLinxPiCloudAuthFetch({
|
|
209
|
+
issuerUrl: options.providerConfig?.issuerUrl,
|
|
210
|
+
getPodDataSession: options.getPodDataSession,
|
|
211
|
+
});
|
|
212
|
+
await syncProviderModels({ runtimeFetch: authFetch });
|
|
213
|
+
return {
|
|
214
|
+
type: 'oauth',
|
|
215
|
+
refresh: credentials.refresh,
|
|
216
|
+
access: LINX_RUNTIME_MANAGED_AUTH_KEY,
|
|
217
|
+
expires: Date.now() + 60 * 60 * 1000,
|
|
218
|
+
};
|
|
156
219
|
},
|
|
157
220
|
getApiKey(credentials) {
|
|
158
|
-
return credentials.access
|
|
221
|
+
return credentials.access === LINX_RUNTIME_MANAGED_AUTH_KEY
|
|
222
|
+
? LINX_RUNTIME_MANAGED_AUTH_KEY
|
|
223
|
+
: LINX_RUNTIME_MANAGED_AUTH_KEY;
|
|
159
224
|
},
|
|
160
225
|
};
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
: await resolveLinxPiCloudOAuthCredential(options.providerConfig?.issuerUrl).catch((error) => {
|
|
164
|
-
if (isOidcLoginExpiredError(error)) {
|
|
165
|
-
return null;
|
|
166
|
-
}
|
|
167
|
-
throw error;
|
|
168
|
-
});
|
|
226
|
+
const storedCredentials = options.providerConfig?.oauth ? null : loadCredentials();
|
|
227
|
+
const hasManagedPodSession = !options.providerConfig?.oauth && Boolean(options.getPodDataSession);
|
|
169
228
|
const explicitOAuthCredential = options.providerConfig?.oauth
|
|
170
229
|
? await options.providerConfig.oauth.login()
|
|
171
230
|
: null;
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
231
|
+
if (storedCredentials || hasManagedPodSession) {
|
|
232
|
+
const authFetch = await resolveLinxPiCloudAuthFetch({
|
|
233
|
+
issuerUrl: options.providerConfig?.issuerUrl,
|
|
234
|
+
getPodDataSession: options.getPodDataSession,
|
|
235
|
+
});
|
|
236
|
+
await syncProviderModels({ runtimeFetch: authFetch }, { refreshOnAuthExpired: true });
|
|
176
237
|
}
|
|
177
238
|
else if (explicitOAuthCredential?.access) {
|
|
178
|
-
await syncProviderModels(explicitOAuthCredential.access);
|
|
179
|
-
}
|
|
180
|
-
else if (explicitApiKey) {
|
|
181
|
-
await syncProviderModels(explicitApiKey);
|
|
239
|
+
await syncProviderModels({ runtimeFetch: createBearerAuthFetch(explicitOAuthCredential.access) });
|
|
182
240
|
}
|
|
183
241
|
modelRegistry.registerProvider(UNDEFINEDS_PROVIDER_ID, {
|
|
184
242
|
api: UNDEFINEDS_PROVIDER_API,
|
|
185
243
|
baseUrl,
|
|
186
|
-
apiKey: 'LINX_RUNTIME_AUTH',
|
|
244
|
+
apiKey: '$LINX_RUNTIME_AUTH',
|
|
187
245
|
oauth: linxOAuthProvider,
|
|
188
246
|
authHeader: false,
|
|
189
247
|
streamSimple: streamAdapter.streamFn,
|
|
190
248
|
models: providerModels,
|
|
191
249
|
});
|
|
192
|
-
if (!options.providerConfig?.oauth
|
|
193
|
-
authStorage.setRuntimeApiKey(UNDEFINEDS_PROVIDER_ID,
|
|
250
|
+
if (!options.providerConfig?.oauth) {
|
|
251
|
+
authStorage.setRuntimeApiKey(UNDEFINEDS_PROVIDER_ID, LINX_RUNTIME_MANAGED_AUTH_KEY);
|
|
194
252
|
}
|
|
195
253
|
if (options.providerConfig?.oauth && explicitOAuthCredential) {
|
|
196
254
|
authStorage.set(UNDEFINEDS_PROVIDER_ID, { type: 'oauth', ...explicitOAuthCredential });
|
|
197
255
|
}
|
|
198
|
-
else if (resolvedOAuth) {
|
|
199
|
-
authStorage.set(UNDEFINEDS_PROVIDER_ID, resolvedOAuth);
|
|
200
|
-
}
|
|
201
256
|
const settingsManager = SettingsManager.create(context.cwd, context.agentDir);
|
|
202
257
|
ensureLinxPiTheme(context.agentDir);
|
|
258
|
+
ensurePiWebAccessConfig();
|
|
203
259
|
settingsManager.setTheme('linx');
|
|
204
260
|
const defaultModelId = sanitizeLinxCloudDefaults(settingsManager, requestedModel, providerModels);
|
|
205
261
|
activeModelId = defaultModelId;
|
|
206
262
|
const bundledSkillsDir = resolveBundledLinxSkillsDir();
|
|
263
|
+
const marketSkillDirs = resolveInstalledMarketSkillDirs();
|
|
264
|
+
const additionalSkillPaths = [
|
|
265
|
+
...(bundledSkillsDir ? [bundledSkillsDir] : []),
|
|
266
|
+
...marketSkillDirs,
|
|
267
|
+
];
|
|
268
|
+
const bundledPackagePaths = [
|
|
269
|
+
resolveBundledPiPackageRoot(LINX_WEB_ACCESS_PACKAGE_SOURCE),
|
|
270
|
+
].filter((path) => Boolean(path));
|
|
207
271
|
const services = await createAgentSessionServices({
|
|
208
272
|
cwd: context.cwd,
|
|
209
273
|
agentDir: context.agentDir,
|
|
@@ -211,48 +275,29 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
211
275
|
settingsManager,
|
|
212
276
|
modelRegistry,
|
|
213
277
|
resourceLoaderOptions: {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
278
|
+
// Built-in: pi-web-access handles web_search, fetch_content, and related web tools.
|
|
279
|
+
additionalExtensionPaths: bundledPackagePaths,
|
|
280
|
+
additionalSkillPaths,
|
|
281
|
+
skillsOverride: (base) => withLinxSkillSourceInfo(base, {
|
|
282
|
+
bundledSkillsDir,
|
|
283
|
+
marketSkillDirs,
|
|
284
|
+
}),
|
|
218
285
|
systemPromptOverride: overrideLinxSystemPrompt,
|
|
219
286
|
},
|
|
220
287
|
});
|
|
221
288
|
const selectedModel = modelRegistry.find(UNDEFINEDS_PROVIDER_ID, defaultModelId)
|
|
222
289
|
?? modelRegistry.getAvailable().find((candidate) => candidate.provider === UNDEFINEDS_PROVIDER_ID);
|
|
223
290
|
if (!selectedModel) {
|
|
224
|
-
throw new Error('Failed to resolve undefineds model from the LinX
|
|
291
|
+
throw new Error('Failed to resolve undefineds model from the LinX runtime adapter');
|
|
225
292
|
}
|
|
226
293
|
const created = await createAgentSessionFromServices({
|
|
227
294
|
services,
|
|
228
295
|
sessionManager: context.sessionManager,
|
|
229
296
|
sessionStartEvent: context.sessionStartEvent,
|
|
230
297
|
model: selectedModel,
|
|
231
|
-
tools: [
|
|
232
|
-
'read',
|
|
233
|
-
'bash',
|
|
234
|
-
'edit',
|
|
235
|
-
'write',
|
|
236
|
-
'web_fetch',
|
|
237
|
-
'web_search',
|
|
238
|
-
'pod_read',
|
|
239
|
-
'pod_write',
|
|
240
|
-
],
|
|
241
|
-
customTools: [
|
|
242
|
-
createLinxPiBashToolDefinition(context.cwd),
|
|
243
|
-
webFetchTool,
|
|
244
|
-
webSearchTool,
|
|
245
|
-
podReadTool,
|
|
246
|
-
podWriteTool,
|
|
247
|
-
],
|
|
248
298
|
});
|
|
249
299
|
const session = created.session;
|
|
250
300
|
enableLinxXhighThinking(session);
|
|
251
|
-
restoreLinxThinkingDefault(session, settingsManager);
|
|
252
|
-
installLinxPiRemoteApproval({
|
|
253
|
-
session,
|
|
254
|
-
cwd: context.cwd,
|
|
255
|
-
});
|
|
256
301
|
if (session.model?.provider !== selectedModel.provider || session.model.id !== selectedModel.id) {
|
|
257
302
|
await session.setModel(selectedModel);
|
|
258
303
|
}
|
|
@@ -267,14 +312,29 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
267
312
|
sessionManager: context.sessionManager,
|
|
268
313
|
sessionStartEvent: context.sessionStartEvent,
|
|
269
314
|
});
|
|
315
|
+
runtime.backend = 'linx';
|
|
316
|
+
runtime.runtimeBackend = workerBackend;
|
|
317
|
+
runtime.autoEnabled = options.autoEnabled === true;
|
|
318
|
+
runtime.symphonyEnabled = options.symphonyEnabled === true;
|
|
270
319
|
runtime.linxAuthBridge = {
|
|
271
320
|
description: UNDEFINEDS_AUTH_BRIDGE_ID,
|
|
272
|
-
authMode,
|
|
273
321
|
providerId: UNDEFINEDS_PROVIDER_ID,
|
|
274
322
|
providerLabel: UNDEFINEDS_PROVIDER_LABEL,
|
|
275
323
|
runtimeUrl: baseUrl,
|
|
276
324
|
shouldPromptLoginOnStart,
|
|
277
325
|
};
|
|
326
|
+
if (backendCommandRouter) {
|
|
327
|
+
;
|
|
328
|
+
runtime.backendCommandRouter = backendCommandRouter;
|
|
329
|
+
}
|
|
330
|
+
if (proxy) {
|
|
331
|
+
;
|
|
332
|
+
runtime.backendSessionRef = proxy.record;
|
|
333
|
+
}
|
|
334
|
+
if (options.getPodDataSession) {
|
|
335
|
+
;
|
|
336
|
+
runtime.getPodDataSession = options.getPodDataSession;
|
|
337
|
+
}
|
|
278
338
|
return runtime;
|
|
279
339
|
},
|
|
280
340
|
async start() {
|
|
@@ -284,17 +344,17 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
284
344
|
await proxy?.close();
|
|
285
345
|
},
|
|
286
346
|
};
|
|
287
|
-
async function syncProviderModels(
|
|
288
|
-
if (!
|
|
347
|
+
async function syncProviderModels(authSession, options = {}) {
|
|
348
|
+
if (!dependencies.listRemoteModels) {
|
|
289
349
|
return;
|
|
290
350
|
}
|
|
291
|
-
const remoteModels = await listRemoteModelsWithAuthRecovery(
|
|
351
|
+
const remoteModels = await listRemoteModelsWithAuthRecovery(authSession.runtimeFetch, options);
|
|
292
352
|
if (remoteModels.length === 0) {
|
|
293
353
|
return;
|
|
294
354
|
}
|
|
295
355
|
const mergedModels = mergeLinxProviderModels(remoteModels.map((entry) => ({
|
|
296
356
|
id: entry.id,
|
|
297
|
-
contextWindow: entry.contextWindow
|
|
357
|
+
contextWindow: entry.contextWindow,
|
|
298
358
|
})), activeModelId);
|
|
299
359
|
const nextModels = mergedModels.map((entry) => buildProviderModel(entry));
|
|
300
360
|
providerModels.splice(0, providerModels.length, ...nextModels);
|
|
@@ -302,29 +362,24 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
302
362
|
activeModelId = resolvePreferredLinxCloudModelId(nextModels, activeModelId);
|
|
303
363
|
}
|
|
304
364
|
}
|
|
305
|
-
async function listRemoteModelsWithAuthRecovery(
|
|
365
|
+
async function listRemoteModelsWithAuthRecovery(authFetch, recoveryOptions) {
|
|
306
366
|
try {
|
|
307
|
-
return await dependencies.listRemoteModels(
|
|
367
|
+
return await dependencies.listRemoteModels(authFetch, baseUrl, { fallback: false, timeoutMs: 5000 });
|
|
308
368
|
}
|
|
309
369
|
catch (error) {
|
|
310
370
|
if (!isAuthExpiredError(error)) {
|
|
311
371
|
return [];
|
|
312
372
|
}
|
|
313
373
|
if (recoveryOptions.refreshOnAuthExpired) {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
374
|
+
try {
|
|
375
|
+
const refreshedAuthFetch = await resolveRefreshedLinxPiCloudAuthFetch();
|
|
376
|
+
if (refreshedAuthFetch) {
|
|
377
|
+
return await dependencies.listRemoteModels(refreshedAuthFetch, baseUrl, { fallback: false, timeoutMs: 5000 });
|
|
317
378
|
}
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
return await dependencies.listRemoteModels(null, baseUrl, refreshed.access);
|
|
323
|
-
}
|
|
324
|
-
catch (retryError) {
|
|
325
|
-
if (!isAuthExpiredError(retryError)) {
|
|
326
|
-
return [];
|
|
327
|
-
}
|
|
379
|
+
}
|
|
380
|
+
catch (retryError) {
|
|
381
|
+
if (!isAuthExpiredError(retryError)) {
|
|
382
|
+
return [];
|
|
328
383
|
}
|
|
329
384
|
}
|
|
330
385
|
}
|
|
@@ -335,39 +390,48 @@ export function createPiRuntimeAdapter(dependencies, options = {}) {
|
|
|
335
390
|
return [];
|
|
336
391
|
}
|
|
337
392
|
}
|
|
338
|
-
async function completeWithAuthRecovery(
|
|
393
|
+
async function completeWithAuthRecovery(authFetch, request) {
|
|
339
394
|
try {
|
|
340
395
|
return await dependencies.createRemoteCompletion({
|
|
341
396
|
...request,
|
|
342
|
-
|
|
397
|
+
authFetch,
|
|
343
398
|
});
|
|
344
399
|
}
|
|
345
400
|
catch (error) {
|
|
346
401
|
if (!isAuthExpiredError(error)) {
|
|
347
402
|
throw error;
|
|
348
403
|
}
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
return null;
|
|
352
|
-
}
|
|
353
|
-
throw refreshError;
|
|
354
|
-
});
|
|
355
|
-
if (!refreshed?.access) {
|
|
404
|
+
const refreshedAuthFetch = await resolveRefreshedLinxPiCloudAuthFetch();
|
|
405
|
+
if (!refreshedAuthFetch) {
|
|
356
406
|
throw error;
|
|
357
407
|
}
|
|
358
408
|
return dependencies.createRemoteCompletion({
|
|
359
409
|
...request,
|
|
360
|
-
|
|
410
|
+
authFetch: refreshedAuthFetch,
|
|
361
411
|
});
|
|
362
412
|
}
|
|
363
413
|
}
|
|
414
|
+
async function resolveRefreshedLinxPiCloudAuthFetch() {
|
|
415
|
+
clearDefaultPodDataSession();
|
|
416
|
+
const storedCredentials = loadCredentials();
|
|
417
|
+
if (storedCredentials || options.getPodDataSession) {
|
|
418
|
+
return resolveLinxPiCloudAuthFetch({
|
|
419
|
+
issuerUrl: options.providerConfig?.issuerUrl,
|
|
420
|
+
getPodDataSession: options.getPodDataSession,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
364
425
|
}
|
|
426
|
+
/** @deprecated Use createLinxRuntimeAdapter. */
|
|
427
|
+
export const createPiRuntimeAdapter = createLinxRuntimeAdapter;
|
|
365
428
|
export function resolveBundledLinxSkillsDir(importMetaUrl = import.meta.url) {
|
|
366
429
|
const moduleDir = dirname(fileURLToPath(importMetaUrl));
|
|
367
430
|
const candidates = [
|
|
368
|
-
// Published package: dist/lib/pi-adapter/runtime.js -> dist/skills
|
|
431
|
+
// Published package: dist/lib/pi-adapter/runtime.js -> dist/skills.
|
|
432
|
+
// This directory is a curated product-skill bundle, not the repo skill root.
|
|
369
433
|
join(moduleDir, '..', '..', 'skills'),
|
|
370
|
-
// Test/dev bundle: <tmp>/dist/lib/pi-adapter/runtime.js ->
|
|
434
|
+
// Test/dev bundle: <tmp>/dist/lib/pi-adapter/runtime.js -> curated product skills.
|
|
371
435
|
resolve(moduleDir, '..', '..', '..', '..', 'skills'),
|
|
372
436
|
// Source-tree fallback when running through a TS loader.
|
|
373
437
|
resolve(moduleDir, '..', '..', '..', '..', '..', 'skills'),
|
|
@@ -379,34 +443,156 @@ export function resolveBundledLinxSkillsDir(importMetaUrl = import.meta.url) {
|
|
|
379
443
|
}
|
|
380
444
|
return null;
|
|
381
445
|
}
|
|
382
|
-
function
|
|
446
|
+
function ensurePiWebAccessConfig() {
|
|
447
|
+
const config = JSON.stringify({ workflow: 'none' }, null, 2) + '\n';
|
|
448
|
+
const linxPath = getSolidLinxPiWebAccessConfigPath();
|
|
449
|
+
const linxDir = getSolidLinxAppDir();
|
|
450
|
+
if (!existsSync(linxDir)) {
|
|
451
|
+
mkdirSync(linxDir, { recursive: true });
|
|
452
|
+
}
|
|
453
|
+
if (!existsSync(linxPath))
|
|
454
|
+
writeFileSync(linxPath, config);
|
|
455
|
+
}
|
|
456
|
+
export function resolveBundledPiPackageRoot(packageName, importMetaUrl = import.meta.url) {
|
|
457
|
+
const moduleDir = dirname(fileURLToPath(importMetaUrl));
|
|
458
|
+
const vendoredCandidates = [
|
|
459
|
+
// Published/built package: dist/lib/pi-adapter/runtime.js -> vendor/<package>.
|
|
460
|
+
resolve(moduleDir, '..', '..', '..', 'vendor', packageName),
|
|
461
|
+
// Defensive fallback for layouts that place vendor under dist.
|
|
462
|
+
resolve(moduleDir, '..', '..', 'vendor', packageName),
|
|
463
|
+
];
|
|
464
|
+
for (const candidate of vendoredCandidates) {
|
|
465
|
+
if (existsSync(join(candidate, 'package.json'))) {
|
|
466
|
+
return candidate;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
try {
|
|
470
|
+
const requireFromRuntime = createRequire(importMetaUrl);
|
|
471
|
+
return dirname(requireFromRuntime.resolve(`${packageName}/package.json`));
|
|
472
|
+
}
|
|
473
|
+
catch {
|
|
474
|
+
return null;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
function withLinxSkillSourceInfo(base, options) {
|
|
478
|
+
const { bundledSkillsDir, marketSkillDirs } = options;
|
|
479
|
+
const filteredSkills = base.skills.filter((skill) => (!bundledSkillsDir
|
|
480
|
+
|| !skill.filePath.startsWith(bundledSkillsDir)
|
|
481
|
+
|| LINX_PRODUCT_SKILL_NAMES.has(skill.name)));
|
|
383
482
|
return {
|
|
384
483
|
...base,
|
|
385
|
-
skills:
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
484
|
+
skills: filteredSkills.map((skill) => {
|
|
485
|
+
if (bundledSkillsDir && skill.filePath.startsWith(bundledSkillsDir)) {
|
|
486
|
+
return {
|
|
487
|
+
...skill,
|
|
488
|
+
sourceInfo: {
|
|
489
|
+
path: skill.filePath,
|
|
490
|
+
source: LINX_PACKAGE_SOURCE,
|
|
491
|
+
scope: 'temporary',
|
|
492
|
+
origin: 'package',
|
|
493
|
+
baseDir: bundledSkillsDir,
|
|
494
|
+
},
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
const marketSkillDir = marketSkillDirs.find((dir) => skill.filePath.startsWith(dir));
|
|
498
|
+
if (marketSkillDir) {
|
|
499
|
+
return {
|
|
500
|
+
...skill,
|
|
501
|
+
sourceInfo: {
|
|
502
|
+
path: skill.filePath,
|
|
503
|
+
source: MARKET_XPOD_CLI_SKILL_SOURCE,
|
|
504
|
+
scope: 'temporary',
|
|
505
|
+
origin: 'marketplace',
|
|
506
|
+
version: resolveMarketSkillVersion(marketSkillDir),
|
|
507
|
+
baseDir: marketSkillDir,
|
|
508
|
+
},
|
|
509
|
+
};
|
|
395
510
|
}
|
|
396
|
-
|
|
511
|
+
return skill;
|
|
512
|
+
}),
|
|
397
513
|
};
|
|
398
514
|
}
|
|
515
|
+
export function resolveInstalledMarketSkillDirs() {
|
|
516
|
+
return [resolveInstalledXpodCliMarketSkillDir()].filter((path) => Boolean(path));
|
|
517
|
+
}
|
|
518
|
+
function resolveInstalledXpodCliMarketSkillDir() {
|
|
519
|
+
const codexHome = process.env.CODEX_HOME?.trim() || join(homedir(), '.codex');
|
|
520
|
+
const versionsRoot = join(codexHome, 'plugins', 'cache', 'undefineds', 'xpod-cli');
|
|
521
|
+
if (!existsSync(versionsRoot)) {
|
|
522
|
+
return null;
|
|
523
|
+
}
|
|
524
|
+
const candidates = [];
|
|
525
|
+
for (const entry of safeReadDir(versionsRoot)) {
|
|
526
|
+
const versionDir = join(versionsRoot, entry);
|
|
527
|
+
if (!safeIsDirectory(versionDir)) {
|
|
528
|
+
continue;
|
|
529
|
+
}
|
|
530
|
+
const skillDir = join(versionDir, 'skills', 'xpod-cli');
|
|
531
|
+
if (existsSync(join(skillDir, 'SKILL.md'))) {
|
|
532
|
+
candidates.push({ version: entry, dir: skillDir });
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
candidates.sort((a, b) => compareVersionLike(b.version, a.version));
|
|
536
|
+
return candidates[0]?.dir ?? null;
|
|
537
|
+
}
|
|
538
|
+
function resolveMarketSkillVersion(skillDir) {
|
|
539
|
+
const version = basename(dirname(dirname(skillDir)));
|
|
540
|
+
return version && version !== 'skills' ? version : undefined;
|
|
541
|
+
}
|
|
542
|
+
function safeReadDir(dir) {
|
|
543
|
+
try {
|
|
544
|
+
return readdirSync(dir);
|
|
545
|
+
}
|
|
546
|
+
catch {
|
|
547
|
+
return [];
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function safeIsDirectory(path) {
|
|
551
|
+
try {
|
|
552
|
+
return statSync(path).isDirectory();
|
|
553
|
+
}
|
|
554
|
+
catch {
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
function compareVersionLike(a, b) {
|
|
559
|
+
const left = a.split(/[.-]/u).map((part) => Number(part));
|
|
560
|
+
const right = b.split(/[.-]/u).map((part) => Number(part));
|
|
561
|
+
const length = Math.max(left.length, right.length);
|
|
562
|
+
for (let i = 0; i < length; i += 1) {
|
|
563
|
+
const l = Number.isFinite(left[i]) ? left[i] : 0;
|
|
564
|
+
const r = Number.isFinite(right[i]) ? right[i] : 0;
|
|
565
|
+
if (l !== r) {
|
|
566
|
+
return l - r;
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
return a.localeCompare(b);
|
|
570
|
+
}
|
|
571
|
+
function enableLinxXhighThinking(session) {
|
|
572
|
+
const originalSupportsXhighThinking = session.supportsXhighThinking?.bind(session);
|
|
573
|
+
const originalGetAvailableThinkingLevels = session.getAvailableThinkingLevels?.bind(session);
|
|
574
|
+
session.supportsXhighThinking = () => (session.model?.provider === UNDEFINEDS_PROVIDER_ID && session.model.reasoning
|
|
575
|
+
? (session.getAvailableThinkingLevels?.().includes('xhigh') ?? true)
|
|
576
|
+
: (originalSupportsXhighThinking?.() ?? false));
|
|
577
|
+
if (originalGetAvailableThinkingLevels) {
|
|
578
|
+
session.getAvailableThinkingLevels = () => {
|
|
579
|
+
const levels = originalGetAvailableThinkingLevels();
|
|
580
|
+
if (session.model?.provider === UNDEFINEDS_PROVIDER_ID && session.model.reasoning && !levels.includes('xhigh')) {
|
|
581
|
+
return [...levels, 'xhigh'];
|
|
582
|
+
}
|
|
583
|
+
return levels;
|
|
584
|
+
};
|
|
585
|
+
}
|
|
586
|
+
}
|
|
399
587
|
export function createLinxPiCodingTools(cwd, options = {}) {
|
|
400
588
|
const localBashOperations = options.bashOperations ?? createLocalBashOperations();
|
|
401
589
|
const bashTimeoutSeconds = options.bashTimeoutSeconds ?? DEFAULT_LINX_PI_BASH_TIMEOUT_SECONDS;
|
|
402
590
|
return createCodingTools(cwd, {
|
|
403
591
|
bash: {
|
|
404
|
-
commandPrefix: buildLinxToolCommandPrefix(),
|
|
405
592
|
operations: {
|
|
406
593
|
exec(command, workingDirectory, options) {
|
|
407
|
-
return localBashOperations.exec(command, workingDirectory ??
|
|
594
|
+
return localBashOperations.exec(command, workingDirectory ?? cwd, {
|
|
408
595
|
...options,
|
|
409
|
-
env: withLinxToolPath(options.env),
|
|
410
596
|
timeout: typeof options.timeout === 'number'
|
|
411
597
|
? options.timeout
|
|
412
598
|
: bashTimeoutSeconds,
|
|
@@ -416,104 +602,147 @@ export function createLinxPiCodingTools(cwd, options = {}) {
|
|
|
416
602
|
},
|
|
417
603
|
});
|
|
418
604
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
env: withLinxToolPath(options.env),
|
|
429
|
-
timeout: typeof options.timeout === 'number'
|
|
430
|
-
? options.timeout
|
|
431
|
-
: bashTimeoutSeconds,
|
|
432
|
-
});
|
|
433
|
-
},
|
|
434
|
-
},
|
|
435
|
-
});
|
|
605
|
+
function isAuthExpiredError(error) {
|
|
606
|
+
return isRemoteAuthExpiredError(error);
|
|
607
|
+
}
|
|
608
|
+
function createBearerAuthFetch(apiKey) {
|
|
609
|
+
return async (url, init) => {
|
|
610
|
+
const headers = new Headers(init?.headers);
|
|
611
|
+
headers.set('Authorization', `Bearer ${apiKey}`);
|
|
612
|
+
return fetch(url, { ...init, headers });
|
|
613
|
+
};
|
|
436
614
|
}
|
|
437
|
-
function
|
|
438
|
-
const
|
|
439
|
-
if (!
|
|
440
|
-
return
|
|
615
|
+
function resolveRuntimeAuthFetchFromApiKey(apiKey) {
|
|
616
|
+
const trimmed = apiKey?.trim();
|
|
617
|
+
if (!trimmed || trimmed === LINX_RUNTIME_MANAGED_AUTH_KEY) {
|
|
618
|
+
return null;
|
|
441
619
|
}
|
|
442
|
-
return
|
|
620
|
+
return createBearerAuthFetch(trimmed);
|
|
443
621
|
}
|
|
444
|
-
function
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
if (!udfsBinDir) {
|
|
448
|
-
return nextEnv;
|
|
622
|
+
async function resolveLinxPiCloudAuthFetch(options) {
|
|
623
|
+
if (options.getPodDataSession) {
|
|
624
|
+
return createPodDataSessionAuthFetch(options.getPodDataSession);
|
|
449
625
|
}
|
|
450
|
-
const
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
if (!entries.includes(udfsBinDir)) {
|
|
454
|
-
nextEnv[pathKey] = [udfsBinDir, currentPath].filter(Boolean).join(delimiter);
|
|
626
|
+
const session = await getDefaultPodDataSession();
|
|
627
|
+
if (session) {
|
|
628
|
+
return withCloudCompletionTimeout(session.runtimeFetch);
|
|
455
629
|
}
|
|
456
|
-
|
|
630
|
+
throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
|
|
457
631
|
}
|
|
458
|
-
function
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
632
|
+
function createPodDataSessionAuthFetch(getPodDataSession) {
|
|
633
|
+
if (getPodDataSession !== getDefaultPodDataSession) {
|
|
634
|
+
return withCloudCompletionTimeout(async (url, init) => {
|
|
635
|
+
const session = await getPodDataSession();
|
|
636
|
+
if (session) {
|
|
637
|
+
try {
|
|
638
|
+
return await session.runtimeFetch(url, init);
|
|
639
|
+
}
|
|
640
|
+
finally {
|
|
641
|
+
await session.close().catch(() => undefined);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
|
|
645
|
+
});
|
|
462
646
|
}
|
|
463
|
-
|
|
647
|
+
let cachedSession = null;
|
|
648
|
+
let cachedSessionPromise = null;
|
|
649
|
+
const getCachedSession = async () => {
|
|
650
|
+
if (cachedSession) {
|
|
651
|
+
return cachedSession;
|
|
652
|
+
}
|
|
653
|
+
if (!cachedSessionPromise) {
|
|
654
|
+
cachedSessionPromise = getPodDataSession().then((session) => {
|
|
655
|
+
cachedSession = session;
|
|
656
|
+
return session;
|
|
657
|
+
}).finally(() => {
|
|
658
|
+
cachedSessionPromise = null;
|
|
659
|
+
});
|
|
660
|
+
}
|
|
661
|
+
return cachedSessionPromise;
|
|
662
|
+
};
|
|
663
|
+
return withCloudCompletionTimeout(async (url, init) => {
|
|
664
|
+
const session = await getCachedSession();
|
|
665
|
+
if (session) {
|
|
666
|
+
return await session.runtimeFetch(url, init);
|
|
667
|
+
}
|
|
668
|
+
throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
|
|
669
|
+
});
|
|
464
670
|
}
|
|
465
|
-
function
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
671
|
+
function withCloudCompletionTimeout(fetcher) {
|
|
672
|
+
return async (url, init) => {
|
|
673
|
+
if (!isChatCompletionRuntimeUrl(String(url))) {
|
|
674
|
+
return fetcher(url, init);
|
|
675
|
+
}
|
|
676
|
+
const timeoutMs = resolveLinxCloudCompletionTimeoutMs();
|
|
677
|
+
const controller = new AbortController();
|
|
678
|
+
const signal = init?.signal
|
|
679
|
+
? combineAbortSignals(init.signal, controller.signal)
|
|
680
|
+
: controller.signal;
|
|
681
|
+
let timedOut = false;
|
|
682
|
+
const timer = setTimeout(() => {
|
|
683
|
+
timedOut = true;
|
|
684
|
+
controller.abort();
|
|
685
|
+
}, timeoutMs);
|
|
686
|
+
try {
|
|
687
|
+
return await Promise.race([
|
|
688
|
+
fetcher(url, { ...init, signal }),
|
|
689
|
+
new Promise((_resolve, reject) => {
|
|
690
|
+
controller.signal.addEventListener('abort', () => {
|
|
691
|
+
if (timedOut) {
|
|
692
|
+
reject(new RemoteChatRequestError(formatLinxCloudTransientMessage(`Request exceeded ${formatTimeoutSeconds(timeoutMs)}s.`), 0, `Timed out waiting for POST ${url}`));
|
|
693
|
+
}
|
|
694
|
+
}, { once: true });
|
|
695
|
+
}),
|
|
696
|
+
]);
|
|
697
|
+
}
|
|
698
|
+
catch (error) {
|
|
699
|
+
if (timedOut) {
|
|
700
|
+
throw new RemoteChatRequestError(formatLinxCloudTransientMessage(`Request exceeded ${formatTimeoutSeconds(timeoutMs)}s.`), 0, error instanceof Error ? error.message : String(error));
|
|
701
|
+
}
|
|
702
|
+
throw error;
|
|
703
|
+
}
|
|
704
|
+
finally {
|
|
705
|
+
clearTimeout(timer);
|
|
706
|
+
}
|
|
707
|
+
};
|
|
476
708
|
}
|
|
477
|
-
function
|
|
478
|
-
|
|
709
|
+
function resolveLinxCloudCompletionTimeoutMs() {
|
|
710
|
+
const raw = process.env.LINX_CHAT_TIMEOUT_MS;
|
|
711
|
+
if (!raw) {
|
|
712
|
+
return DEFAULT_LINX_CLOUD_COMPLETION_TIMEOUT_MS;
|
|
713
|
+
}
|
|
714
|
+
const parsed = Number(raw);
|
|
715
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_LINX_CLOUD_COMPLETION_TIMEOUT_MS;
|
|
479
716
|
}
|
|
480
|
-
function
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
: originalSupportsThinking());
|
|
717
|
+
function formatTimeoutSeconds(timeoutMs) {
|
|
718
|
+
return Math.max(1, Math.round(timeoutMs / 1000));
|
|
719
|
+
}
|
|
720
|
+
function isChatCompletionRuntimeUrl(value) {
|
|
721
|
+
try {
|
|
722
|
+
const target = new URL(value);
|
|
723
|
+
const segments = target.pathname.split('/').filter(Boolean);
|
|
724
|
+
return segments.length >= 3
|
|
725
|
+
&& /^v\d+$/.test(segments.at(-3) ?? '')
|
|
726
|
+
&& segments.at(-2) === 'chat'
|
|
727
|
+
&& segments.at(-1) === 'completions';
|
|
492
728
|
}
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const levels = originalGetAvailableThinkingLevels();
|
|
496
|
-
if (supportsLinxXhigh() && !levels.includes('xhigh')) {
|
|
497
|
-
return [...levels, 'xhigh'];
|
|
498
|
-
}
|
|
499
|
-
return levels;
|
|
500
|
-
};
|
|
729
|
+
catch {
|
|
730
|
+
return false;
|
|
501
731
|
}
|
|
502
732
|
}
|
|
503
|
-
function
|
|
504
|
-
if (
|
|
505
|
-
return;
|
|
733
|
+
function combineAbortSignals(left, right) {
|
|
734
|
+
if (typeof AbortSignal.any === 'function') {
|
|
735
|
+
return AbortSignal.any([left, right]);
|
|
506
736
|
}
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
737
|
+
const controller = new AbortController();
|
|
738
|
+
const abort = () => controller.abort();
|
|
739
|
+
if (left.aborted || right.aborted) {
|
|
740
|
+
abort();
|
|
741
|
+
return controller.signal;
|
|
510
742
|
}
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
function isAuthExpiredError(error) {
|
|
516
|
-
return isRemoteAuthExpiredError(error);
|
|
743
|
+
left.addEventListener('abort', abort, { once: true });
|
|
744
|
+
right.addEventListener('abort', abort, { once: true });
|
|
745
|
+
return controller.signal;
|
|
517
746
|
}
|
|
518
747
|
function sanitizeLinxCloudDefaults(settingsManager, requestedModel, providerModels) {
|
|
519
748
|
const availableModelIds = new Set(providerModels.map((model) => model.id));
|
|
@@ -532,8 +761,11 @@ function buildProviderModel(input) {
|
|
|
532
761
|
return {
|
|
533
762
|
id: input.id,
|
|
534
763
|
name: input.id,
|
|
535
|
-
api:
|
|
764
|
+
api: UNDEFINEDS_PROVIDER_API,
|
|
536
765
|
reasoning: true,
|
|
766
|
+
thinkingLevelMap: {
|
|
767
|
+
xhigh: 'xhigh',
|
|
768
|
+
},
|
|
537
769
|
input: ['text'],
|
|
538
770
|
cost: {
|
|
539
771
|
input: 0,
|
|
@@ -561,7 +793,7 @@ function mergeLinxProviderModels(models, activeModelId) {
|
|
|
561
793
|
]) {
|
|
562
794
|
byId.set(id, {
|
|
563
795
|
id,
|
|
564
|
-
contextWindow:
|
|
796
|
+
contextWindow: normalizeLinxCloudContextWindow(undefined),
|
|
565
797
|
});
|
|
566
798
|
}
|
|
567
799
|
for (const model of models) {
|
|
@@ -571,20 +803,15 @@ function mergeLinxProviderModels(models, activeModelId) {
|
|
|
571
803
|
}
|
|
572
804
|
byId.set(id, {
|
|
573
805
|
id,
|
|
574
|
-
contextWindow: model.contextWindow,
|
|
806
|
+
contextWindow: normalizeLinxCloudContextWindow(model.contextWindow),
|
|
575
807
|
});
|
|
576
808
|
}
|
|
577
809
|
return [...byId.values()];
|
|
578
810
|
}
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
const credential = await resolveLinxPiCloudOAuthCredential(options.issuerUrl);
|
|
584
|
-
if (credential?.access) {
|
|
585
|
-
return credential.access;
|
|
586
|
-
}
|
|
587
|
-
throw new Error('No LinX cloud login found. Interactive TUI supports /login in-app. For non-interactive --print mode, run `linx login` first.');
|
|
811
|
+
function normalizeLinxCloudContextWindow(contextWindow) {
|
|
812
|
+
return typeof contextWindow === 'number' && Number.isFinite(contextWindow) && contextWindow > 0
|
|
813
|
+
? contextWindow
|
|
814
|
+
: DEFAULT_LINX_CLOUD_CONTEXT_WINDOW;
|
|
588
815
|
}
|
|
589
816
|
function withSystemPrompt(systemPrompt, messages) {
|
|
590
817
|
const prompt = systemPrompt?.trim();
|
|
@@ -603,6 +830,9 @@ function overrideLinxSystemPrompt(base) {
|
|
|
603
830
|
'When replying in Chinese, describe yourself as "AI主理人".',
|
|
604
831
|
'Use a friendly, direct style like: "你好!我是 LinX,一个 AI 主理人,很高兴为你服务!"',
|
|
605
832
|
'Keep Pi-compatible coding agent behavior: read files, run commands, edit code, use tools, and follow project instructions.',
|
|
833
|
+
'When introducing capabilities, describe only user-facing LinX product abilities and the currently available runtime actions.',
|
|
834
|
+
'Do not advertise repository-local agent instructions, internal command names, bundled plugin skill names, package names, or developer-only workflows as features the user can call.',
|
|
835
|
+
'If a capability depends on the current workspace, installed tools, login state, backend, or Symphony mode, state that dependency instead of implying it is always available.',
|
|
606
836
|
].join('\n');
|
|
607
837
|
if (!original) {
|
|
608
838
|
return identity;
|