@undefineds.co/linx 0.3.5 → 0.3.8
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 +336 -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,16 +1,19 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
-
import {
|
|
3
|
-
import { basename, join } from 'node:path';
|
|
2
|
+
import { basename } from 'node:path';
|
|
4
3
|
import { readFileSync } from 'node:fs';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@mariozechner/pi-tui';
|
|
4
|
+
import { keyHint, LoginDialogComponent, rawKeyHint } from '@earendil-works/pi-coding-agent';
|
|
5
|
+
import { Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@earendil-works/pi-tui';
|
|
8
6
|
import { clearAccountSession } from '../account-session.js';
|
|
9
7
|
import { clearCredentials, loadCredentials } from '../credentials-store.js';
|
|
10
8
|
import { clearOidcSessionStorage } from '../oidc-session-storage.js';
|
|
9
|
+
import { persistSolidClientCredentialsLogin } from '../solid-client-credentials-login.js';
|
|
11
10
|
import { extractUsernameFromWebId, resolveProfileDisplayName } from '../profile-identity.js';
|
|
12
11
|
import { LINX_TUI_KEYMAP_COMMAND, LINX_TUI_KEYMAP_LABEL, LINX_TUI_LOGIN_COMMAND } from '../linx-tui-contract.js';
|
|
13
|
-
|
|
12
|
+
import { suppressPodStatusOutput } from './pod-status-output.js';
|
|
13
|
+
import { LINX_RUNTIME_MANAGED_AUTH_KEY } from './runtime.js';
|
|
14
|
+
import { formatLinxCliErrorMessage } from '../linx-cloud-errors.js';
|
|
15
|
+
import { getSolidLinxAgentDir } from '../solid-local-store.js';
|
|
16
|
+
export const LINX_AGENT_DIR = getSolidLinxAgentDir();
|
|
14
17
|
export const LINX_UPDATE_PACKAGE_NAME = '@undefineds.co/linx';
|
|
15
18
|
export const LINX_CHANGELOG_URL = 'https://github.com/undefineds-co/linx-cli/releases';
|
|
16
19
|
export const LINX_CLI_VERSION = readLinxCliVersion();
|
|
@@ -20,9 +23,11 @@ const LINX_AUTH_PENDING_RETRY = Symbol.for('linx.tui.authPendingRetry');
|
|
|
20
23
|
const LINX_AUTH_LOGIN_SCHEDULED = Symbol.for('linx.tui.authLoginScheduled');
|
|
21
24
|
const LINX_AUTH_REPORTING_ERROR = Symbol.for('linx.tui.authReportingError');
|
|
22
25
|
const LINX_UPDATE_IN_PROGRESS = Symbol.for('linx.tui.updateInProgress');
|
|
26
|
+
const LINX_UPDATE_CHECK_SCHEDULED = Symbol.for('linx.tui.updateCheckScheduled');
|
|
27
|
+
const LINX_SUPPRESS_UPSTREAM_PI_UPDATE = Symbol.for('linx.tui.suppressUpstreamPiUpdate');
|
|
23
28
|
const LINX_PROVIDER_ID = 'undefineds';
|
|
24
29
|
const AUTH_OPTION_BROWSER = 'Authorize in browser';
|
|
25
|
-
const
|
|
30
|
+
const AUTH_OPTION_CLIENT_CREDENTIALS = 'Enter Solid client credentials';
|
|
26
31
|
const AUTH_OPTION_EXIT = 'Exit';
|
|
27
32
|
const UPDATE_OPTION_INSTALL = 'Install update and restart';
|
|
28
33
|
const UPDATE_OPTION_CHANGELOG = 'Open changelog';
|
|
@@ -55,34 +60,77 @@ function patchTerminalTitle(interactive) {
|
|
|
55
60
|
};
|
|
56
61
|
}
|
|
57
62
|
function patchVersionCheck(interactive) {
|
|
63
|
+
const originalRun = interactive.run?.bind(interactive);
|
|
64
|
+
if (typeof originalRun === 'function') {
|
|
65
|
+
interactive.run = async function patchedLinxRun(...args) {
|
|
66
|
+
this[LINX_SUPPRESS_UPSTREAM_PI_UPDATE] = true;
|
|
67
|
+
return originalRun(...args);
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
const originalInit = interactive.init?.bind(interactive);
|
|
71
|
+
if (typeof originalInit === 'function') {
|
|
72
|
+
interactive.init = async function patchedLinxVersionInit(...args) {
|
|
73
|
+
await originalInit(...args);
|
|
74
|
+
scheduleLinxVersionCheck(this);
|
|
75
|
+
};
|
|
76
|
+
}
|
|
58
77
|
interactive.checkForNewVersion = async function patchedCheckForNewVersion() {
|
|
59
|
-
|
|
60
|
-
return undefined;
|
|
61
|
-
}
|
|
62
|
-
try {
|
|
63
|
-
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(LINX_UPDATE_PACKAGE_NAME)}/latest`, {
|
|
64
|
-
signal: AbortSignal.timeout(5000),
|
|
65
|
-
});
|
|
66
|
-
if (!response.ok) {
|
|
67
|
-
return undefined;
|
|
68
|
-
}
|
|
69
|
-
const body = await response.json();
|
|
70
|
-
const latest = typeof body.version === 'string' ? body.version.trim() : '';
|
|
71
|
-
if (!latest || !isVersionNewer(latest, LINX_CLI_VERSION)) {
|
|
72
|
-
return undefined;
|
|
73
|
-
}
|
|
74
|
-
return latest;
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return undefined;
|
|
78
|
-
}
|
|
78
|
+
return checkForNewLinxVersion();
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
function patchUpdateNotification(interactive) {
|
|
82
82
|
interactive.showNewVersionNotification = function patchedShowNewVersionNotification(newVersion) {
|
|
83
|
-
|
|
83
|
+
if (this[LINX_SUPPRESS_UPSTREAM_PI_UPDATE]) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (shouldDeferLinxUpdateNotification(this)) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
const normalizedVersion = normalizeLinxUpdateVersion(newVersion);
|
|
90
|
+
if (!normalizedVersion) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
void showLinxUpdateSelector(this, normalizedVersion);
|
|
84
94
|
};
|
|
85
95
|
}
|
|
96
|
+
function scheduleLinxVersionCheck(interactive) {
|
|
97
|
+
if (interactive[LINX_UPDATE_CHECK_SCHEDULED]) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
interactive[LINX_UPDATE_CHECK_SCHEDULED] = true;
|
|
101
|
+
queueMicrotask(() => {
|
|
102
|
+
void checkForNewLinxVersion()
|
|
103
|
+
.then((latest) => {
|
|
104
|
+
if (!latest || shouldDeferLinxUpdateNotification(interactive)) {
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
void showLinxUpdateSelector(interactive, latest);
|
|
108
|
+
})
|
|
109
|
+
.catch(() => undefined);
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
async function checkForNewLinxVersion() {
|
|
113
|
+
if (process.env.PI_OFFLINE) {
|
|
114
|
+
return undefined;
|
|
115
|
+
}
|
|
116
|
+
try {
|
|
117
|
+
const response = await fetch(`https://registry.npmjs.org/${encodeURIComponent(LINX_UPDATE_PACKAGE_NAME)}/latest`, {
|
|
118
|
+
signal: AbortSignal.timeout(5000),
|
|
119
|
+
});
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
return undefined;
|
|
122
|
+
}
|
|
123
|
+
const body = await response.json();
|
|
124
|
+
const latest = typeof body.version === 'string' ? body.version.trim() : '';
|
|
125
|
+
if (!latest || !isVersionNewer(latest, LINX_CLI_VERSION)) {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
return latest;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return undefined;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
86
134
|
async function showLinxUpdateSelector(interactive, newVersion) {
|
|
87
135
|
if (interactive[LINX_UPDATE_IN_PROGRESS]) {
|
|
88
136
|
return;
|
|
@@ -94,10 +142,11 @@ async function showLinxUpdateSelector(interactive, newVersion) {
|
|
|
94
142
|
`Current ${LINX_CLI_VERSION} -> latest ${newVersion}`,
|
|
95
143
|
'Choose how to handle this update.',
|
|
96
144
|
].join('\n');
|
|
97
|
-
const options = [UPDATE_OPTION_INSTALL, UPDATE_OPTION_CHANGELOG
|
|
98
|
-
const
|
|
145
|
+
const options = [UPDATE_OPTION_LATER, UPDATE_OPTION_INSTALL, UPDATE_OPTION_CHANGELOG];
|
|
146
|
+
const rawSelected = typeof interactive.showExtensionSelector === 'function'
|
|
99
147
|
? await interactive.showExtensionSelector(title, options)
|
|
100
148
|
: undefined;
|
|
149
|
+
const selected = normalizeSelectorChoice(rawSelected, options);
|
|
101
150
|
if (selected === UPDATE_OPTION_INSTALL) {
|
|
102
151
|
await installLinxUpdateAndRestart(interactive, newVersion);
|
|
103
152
|
return;
|
|
@@ -117,6 +166,28 @@ async function showLinxUpdateSelector(interactive, newVersion) {
|
|
|
117
166
|
interactive[LINX_UPDATE_IN_PROGRESS] = false;
|
|
118
167
|
}
|
|
119
168
|
}
|
|
169
|
+
function shouldDeferLinxUpdateNotification(interactive) {
|
|
170
|
+
return Boolean(interactive[LINX_AUTH_LOGIN_IN_PROGRESS]
|
|
171
|
+
|| interactive[LINX_AUTH_LOGIN_ON_INIT]
|
|
172
|
+
|| interactive[LINX_AUTH_PENDING_RETRY]
|
|
173
|
+
|| interactive[LINX_AUTH_LOGIN_SCHEDULED]);
|
|
174
|
+
}
|
|
175
|
+
function normalizeLinxUpdateVersion(value) {
|
|
176
|
+
const direct = normalizeNonEmptyString(value);
|
|
177
|
+
if (direct) {
|
|
178
|
+
return direct;
|
|
179
|
+
}
|
|
180
|
+
if (!isRecord(value)) {
|
|
181
|
+
return undefined;
|
|
182
|
+
}
|
|
183
|
+
for (const key of ['version', 'latest', 'latestVersion', 'packageVersion', 'newVersion']) {
|
|
184
|
+
const nested = normalizeNonEmptyString(value[key]);
|
|
185
|
+
if (nested) {
|
|
186
|
+
return nested;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return undefined;
|
|
190
|
+
}
|
|
120
191
|
async function installLinxUpdateAndRestart(interactive, newVersion) {
|
|
121
192
|
interactive.showStatus?.(`Installing LinX ${newVersion}...`);
|
|
122
193
|
interactive.ui?.requestRender?.();
|
|
@@ -229,13 +300,15 @@ function patchAuthExpiredSessionEvents(interactive) {
|
|
|
229
300
|
return;
|
|
230
301
|
}
|
|
231
302
|
interactive.handleEvent = async function patchedHandleEvent(event) {
|
|
232
|
-
|
|
303
|
+
const normalizedEvent = normalizeLinxCliErrorEvent(event);
|
|
304
|
+
if (eventHasLinxAuthExpiredError(normalizedEvent)) {
|
|
305
|
+
showLinxAuthExpiredRecoveryNotice(this);
|
|
233
306
|
prepareLinxAuthExpiredRetry(this);
|
|
234
307
|
suppressLinxAuthExpiredAssistantError(this);
|
|
235
308
|
scheduleLinxCloudLogin(this, 'expired');
|
|
236
309
|
return undefined;
|
|
237
310
|
}
|
|
238
|
-
const result = await originalHandleEvent(
|
|
311
|
+
const result = await originalHandleEvent(normalizedEvent);
|
|
239
312
|
return result;
|
|
240
313
|
};
|
|
241
314
|
}
|
|
@@ -246,9 +319,11 @@ function patchAuthExpiredLoginPrompt(interactive) {
|
|
|
246
319
|
}
|
|
247
320
|
interactive.showError = function patchedShowError(errorMessage) {
|
|
248
321
|
const text = typeof errorMessage === 'string' ? errorMessage : String(errorMessage);
|
|
249
|
-
if (!isLinxAuthExpiredError(text)) {
|
|
250
|
-
return originalShowError(errorMessage);
|
|
322
|
+
if (this[LINX_AUTH_REPORTING_ERROR] || !isLinxAuthExpiredError(text)) {
|
|
323
|
+
return originalShowError(formatLinxCliErrorMessage(errorMessage));
|
|
251
324
|
}
|
|
325
|
+
showLinxAuthExpiredRecoveryNotice(this);
|
|
326
|
+
prepareLinxAuthExpiredRetry(this);
|
|
252
327
|
scheduleLinxCloudLogin(this, 'expired');
|
|
253
328
|
return undefined;
|
|
254
329
|
};
|
|
@@ -264,10 +339,49 @@ function eventHasLinxAuthExpiredError(event) {
|
|
|
264
339
|
return false;
|
|
265
340
|
}
|
|
266
341
|
const message = isRecord(event.message) ? event.message : undefined;
|
|
342
|
+
const topLevelErrorMessage = typeof event.errorMessage === 'string' ? event.errorMessage : '';
|
|
267
343
|
const errorMessage = typeof message?.errorMessage === 'string' ? message.errorMessage : '';
|
|
268
344
|
const error = isRecord(event.error) ? event.error : undefined;
|
|
269
345
|
const nestedErrorMessage = typeof error?.errorMessage === 'string' ? error.errorMessage : '';
|
|
270
|
-
return isLinxAuthExpiredError(`${errorMessage}\n${nestedErrorMessage}`);
|
|
346
|
+
return isLinxAuthExpiredError(`${topLevelErrorMessage}\n${errorMessage}\n${nestedErrorMessage}`);
|
|
347
|
+
}
|
|
348
|
+
function normalizeLinxCliErrorEvent(event) {
|
|
349
|
+
if (typeof event === 'string') {
|
|
350
|
+
const normalized = formatLinxCliErrorMessage(event);
|
|
351
|
+
return normalized === event ? event : normalized;
|
|
352
|
+
}
|
|
353
|
+
if (Array.isArray(event)) {
|
|
354
|
+
let changed = false;
|
|
355
|
+
const next = event.map((item) => {
|
|
356
|
+
const normalized = normalizeLinxCliErrorEvent(item);
|
|
357
|
+
if (normalized !== item) {
|
|
358
|
+
changed = true;
|
|
359
|
+
}
|
|
360
|
+
return normalized;
|
|
361
|
+
});
|
|
362
|
+
return changed ? next : event;
|
|
363
|
+
}
|
|
364
|
+
if (!isRecord(event)) {
|
|
365
|
+
return event;
|
|
366
|
+
}
|
|
367
|
+
let changed = false;
|
|
368
|
+
const next = {};
|
|
369
|
+
for (const [key, value] of Object.entries(event)) {
|
|
370
|
+
const normalized = normalizeLinxCliErrorEvent(value);
|
|
371
|
+
next[key] = normalized;
|
|
372
|
+
if (normalized !== value) {
|
|
373
|
+
changed = true;
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
return changed ? { ...event, ...next } : event;
|
|
377
|
+
}
|
|
378
|
+
function showLinxAuthExpiredRecoveryNotice(interactive) {
|
|
379
|
+
interactive.showStatus?.([
|
|
380
|
+
'LinX Cloud login expired.',
|
|
381
|
+
'Your message reached LinX, but the Cloud token was rejected.',
|
|
382
|
+
'Choose a sign-in method below, or run /login if the selector is not visible.',
|
|
383
|
+
].join('\n'));
|
|
384
|
+
interactive.ui?.requestRender?.();
|
|
271
385
|
}
|
|
272
386
|
async function startLinxCloudLogin(interactive, options = {}) {
|
|
273
387
|
if (interactive[LINX_AUTH_LOGIN_IN_PROGRESS]) {
|
|
@@ -296,8 +410,8 @@ async function startLinxCloudLogin(interactive, options = {}) {
|
|
|
296
410
|
await finishLinxAuthSuccess(interactive, reason, 'Browser authorization complete.');
|
|
297
411
|
return;
|
|
298
412
|
}
|
|
299
|
-
if (selected ===
|
|
300
|
-
await
|
|
413
|
+
if (selected === AUTH_OPTION_CLIENT_CREDENTIALS) {
|
|
414
|
+
await promptForLinxClientCredentials(interactive, reason);
|
|
301
415
|
return;
|
|
302
416
|
}
|
|
303
417
|
if (selected === AUTH_OPTION_EXIT) {
|
|
@@ -358,13 +472,37 @@ function normalizeLinxLoginError(message) {
|
|
|
358
472
|
}
|
|
359
473
|
async function selectLinxAuthMethod(interactive, reason) {
|
|
360
474
|
const title = buildLinxAuthPromptTitle(reason, resolveRuntimeProviderLabel(interactive));
|
|
361
|
-
const options = [AUTH_OPTION_BROWSER,
|
|
475
|
+
const options = [AUTH_OPTION_BROWSER, AUTH_OPTION_CLIENT_CREDENTIALS, AUTH_OPTION_EXIT];
|
|
362
476
|
if (typeof interactive.showExtensionSelector === 'function') {
|
|
363
|
-
return await interactive.showExtensionSelector(title, options);
|
|
477
|
+
return normalizeSelectorChoice(await interactive.showExtensionSelector(title, options), options);
|
|
364
478
|
}
|
|
365
479
|
showLinxAuthFallback(interactive, title, options);
|
|
366
480
|
return undefined;
|
|
367
481
|
}
|
|
482
|
+
function normalizeSelectorChoice(value, options) {
|
|
483
|
+
const direct = matchSelectorChoice(value, options);
|
|
484
|
+
if (direct) {
|
|
485
|
+
return direct;
|
|
486
|
+
}
|
|
487
|
+
if (!isRecord(value)) {
|
|
488
|
+
return undefined;
|
|
489
|
+
}
|
|
490
|
+
for (const key of ['value', 'label', 'title', 'name', 'display', 'text', 'option', 'id']) {
|
|
491
|
+
const match = matchSelectorChoice(value[key], options);
|
|
492
|
+
if (match) {
|
|
493
|
+
return match;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
return undefined;
|
|
497
|
+
}
|
|
498
|
+
function matchSelectorChoice(value, options) {
|
|
499
|
+
const normalized = normalizeNonEmptyString(value);
|
|
500
|
+
if (!normalized) {
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
return options.find((option) => option === normalized)
|
|
504
|
+
?? options.find((option) => stripAnsi(option).trim() === stripAnsi(normalized).trim());
|
|
505
|
+
}
|
|
368
506
|
function buildLinxAuthPromptTitle(reason, providerLabel) {
|
|
369
507
|
if (reason === 'startup') {
|
|
370
508
|
return [
|
|
@@ -377,7 +515,7 @@ function buildLinxAuthPromptTitle(reason, providerLabel) {
|
|
|
377
515
|
return [
|
|
378
516
|
'LinX Cloud login expired',
|
|
379
517
|
'Your current Solid token was rejected by LinX Cloud.',
|
|
380
|
-
'Re-authorize or
|
|
518
|
+
'Re-authorize or enter Solid client credentials, then retry your message.',
|
|
381
519
|
].join('\n');
|
|
382
520
|
}
|
|
383
521
|
return [
|
|
@@ -395,25 +533,37 @@ function showLinxAuthFallback(interactive, title, options) {
|
|
|
395
533
|
].join('\n'), 1, 0));
|
|
396
534
|
interactive.ui?.requestRender?.();
|
|
397
535
|
}
|
|
398
|
-
async function
|
|
536
|
+
async function promptForLinxClientCredentials(interactive, reason) {
|
|
399
537
|
if (typeof interactive.showExtensionInput !== 'function') {
|
|
400
|
-
interactive.showError?.('This terminal build cannot collect
|
|
538
|
+
interactive.showError?.('This terminal build cannot collect Solid client credentials inside the TUI.');
|
|
401
539
|
return;
|
|
402
540
|
}
|
|
403
|
-
const
|
|
404
|
-
reason === 'expired' ? 'Enter
|
|
405
|
-
'
|
|
406
|
-
|
|
407
|
-
|
|
541
|
+
const credentials = await interactive.showExtensionInput([
|
|
542
|
+
reason === 'expired' ? 'Enter Solid client credentials' : 'Use Solid client credentials',
|
|
543
|
+
'This is only for Solid/LinX identity. AI provider keys belong in `linx ai connect`.',
|
|
544
|
+
'Format: client_id:client_secret',
|
|
545
|
+
'Press Escape to cancel.',
|
|
546
|
+
].join('\n'), 'client_id:client_secret');
|
|
547
|
+
const trimmed = typeof credentials === 'string' ? credentials.trim() : '';
|
|
408
548
|
if (!trimmed) {
|
|
409
|
-
interactive.showStatus?.('
|
|
549
|
+
interactive.showStatus?.('Solid client credentials entry cancelled.');
|
|
410
550
|
return;
|
|
411
551
|
}
|
|
552
|
+
const result = await resolveSolidClientCredentialsLogin(interactive)(trimmed);
|
|
412
553
|
const authStorage = interactive.session?.modelRegistry?.authStorage;
|
|
413
|
-
authStorage?.setRuntimeApiKey?.(LINX_PROVIDER_ID,
|
|
414
|
-
authStorage?.set?.(LINX_PROVIDER_ID, {
|
|
554
|
+
authStorage?.setRuntimeApiKey?.(LINX_PROVIDER_ID, LINX_RUNTIME_MANAGED_AUTH_KEY);
|
|
555
|
+
authStorage?.set?.(LINX_PROVIDER_ID, {
|
|
556
|
+
type: 'api_key',
|
|
557
|
+
key: LINX_RUNTIME_MANAGED_AUTH_KEY,
|
|
558
|
+
webId: result.webId,
|
|
559
|
+
podUrl: result.podUrl,
|
|
560
|
+
});
|
|
415
561
|
await refreshLinxAuthState(interactive);
|
|
416
|
-
await finishLinxAuthSuccess(interactive, reason, '
|
|
562
|
+
await finishLinxAuthSuccess(interactive, reason, 'Solid client credentials saved to ~/.solid/auth.');
|
|
563
|
+
}
|
|
564
|
+
function resolveSolidClientCredentialsLogin(interactive) {
|
|
565
|
+
const override = interactive?.__linxPersistSolidClientCredentialsLogin ?? interactive?.__linxPersistSolidSecretLogin;
|
|
566
|
+
return typeof override === 'function' ? override : persistSolidClientCredentialsLogin;
|
|
417
567
|
}
|
|
418
568
|
async function finishLinxAuthSuccess(interactive, reason, detail) {
|
|
419
569
|
const prefix = authStatusPrefix(reason);
|
|
@@ -619,10 +769,13 @@ function extractUserMessageText(message) {
|
|
|
619
769
|
return text || undefined;
|
|
620
770
|
}
|
|
621
771
|
async function refreshLinxAuthState(interactive) {
|
|
772
|
+
clearLinxAuthPromptOnStart(interactive);
|
|
773
|
+
syncRuntimeCredential(interactive);
|
|
622
774
|
interactive.session?.modelRegistry?.refresh?.();
|
|
623
775
|
await interactive.updateAvailableProviderCount?.();
|
|
624
776
|
interactive.ui?.requestRender?.();
|
|
625
777
|
}
|
|
778
|
+
export const __testRefreshLinxAuthState = refreshLinxAuthState;
|
|
626
779
|
function authStatusPrefix(reason) {
|
|
627
780
|
if (reason === 'expired') {
|
|
628
781
|
return 'LinX Cloud login refreshed.';
|
|
@@ -650,7 +803,6 @@ async function runLinxCloudLogin(interactive, authStorage, reason) {
|
|
|
650
803
|
return promptForLinxManualRedirectUrl(interactive, signal);
|
|
651
804
|
},
|
|
652
805
|
});
|
|
653
|
-
syncRuntimeCredential(interactive);
|
|
654
806
|
}
|
|
655
807
|
async function runLinxCloudBrowserLogin(interactive, authStorage, reason) {
|
|
656
808
|
if (canRenderLinxLoginDialog(interactive)) {
|
|
@@ -712,7 +864,6 @@ async function runLinxCloudLoginDialog(interactive, authStorage, reason) {
|
|
|
712
864
|
},
|
|
713
865
|
signal: dialog.signal,
|
|
714
866
|
});
|
|
715
|
-
syncRuntimeCredential(interactive);
|
|
716
867
|
}
|
|
717
868
|
finally {
|
|
718
869
|
restoreEditor();
|
|
@@ -755,13 +906,20 @@ async function promptForLinxManualRedirectUrl(interactive, signal) {
|
|
|
755
906
|
}
|
|
756
907
|
function syncRuntimeCredential(interactive) {
|
|
757
908
|
const authStorage = interactive.session?.modelRegistry?.authStorage;
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
909
|
+
authStorage?.setRuntimeApiKey?.(LINX_PROVIDER_ID, LINX_RUNTIME_MANAGED_AUTH_KEY);
|
|
910
|
+
}
|
|
911
|
+
function clearLinxAuthPromptOnStart(interactive) {
|
|
912
|
+
const candidates = [
|
|
913
|
+
interactive,
|
|
914
|
+
interactive?.runtimeHost,
|
|
915
|
+
interactive?.runtime,
|
|
916
|
+
interactive?.session,
|
|
917
|
+
];
|
|
918
|
+
for (const candidate of candidates) {
|
|
919
|
+
const bridge = candidate?.linxAuthBridge;
|
|
920
|
+
if (bridge && typeof bridge === 'object') {
|
|
921
|
+
bridge.shouldPromptLoginOnStart = false;
|
|
922
|
+
}
|
|
765
923
|
}
|
|
766
924
|
}
|
|
767
925
|
function showLinxLoginUrl(interactive, info) {
|
|
@@ -890,13 +1048,29 @@ export function buildLinxWelcomeCardState(interactive, profileDisplayName = null
|
|
|
890
1048
|
workspace,
|
|
891
1049
|
session,
|
|
892
1050
|
next: [
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
1051
|
+
safeKeyHint('tui.input.submit', 'send'),
|
|
1052
|
+
safeKeyHint('app.model.select', 'model'),
|
|
1053
|
+
safeRawKeyHint(LINX_TUI_LOGIN_COMMAND, 'auth'),
|
|
1054
|
+
safeRawKeyHint(LINX_TUI_KEYMAP_COMMAND, LINX_TUI_KEYMAP_LABEL),
|
|
897
1055
|
].join(' \x1b[2m·\x1b[22m '),
|
|
898
1056
|
};
|
|
899
1057
|
}
|
|
1058
|
+
function safeKeyHint(keybinding, description) {
|
|
1059
|
+
try {
|
|
1060
|
+
return keyHint(keybinding, description);
|
|
1061
|
+
}
|
|
1062
|
+
catch {
|
|
1063
|
+
return `\x1b[2m${keybinding}\x1b[22m \x1b[2m${description}\x1b[22m`;
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
function safeRawKeyHint(key, description) {
|
|
1067
|
+
try {
|
|
1068
|
+
return rawKeyHint(key, description);
|
|
1069
|
+
}
|
|
1070
|
+
catch {
|
|
1071
|
+
return `\x1b[2m${key}\x1b[22m \x1b[2m${description}\x1b[22m`;
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
900
1074
|
function renderField(label, value, width) {
|
|
901
1075
|
const prefix = `\x1b[2m${label}\x1b[22m`;
|
|
902
1076
|
const paddedPrefix = prefix + ' '.repeat(Math.max(1, 10 - visibleWidth(prefix)));
|
|
@@ -967,54 +1141,18 @@ function resolveRuntimeProviderLabel(interactive) {
|
|
|
967
1141
|
if (bridge?.providerLabel) {
|
|
968
1142
|
return bridge.providerLabel;
|
|
969
1143
|
}
|
|
970
|
-
return '
|
|
971
|
-
}
|
|
972
|
-
async function suppressPodStatusOutput(operation) {
|
|
973
|
-
if (process.env.LINX_TUI_SHOW_POD_STATUS === '1') {
|
|
974
|
-
return await operation();
|
|
975
|
-
}
|
|
976
|
-
const restoreStdout = patchPodStatusWriter(process.stdout);
|
|
977
|
-
const restoreStderr = patchPodStatusWriter(process.stderr);
|
|
978
|
-
try {
|
|
979
|
-
return await operation();
|
|
980
|
-
}
|
|
981
|
-
finally {
|
|
982
|
-
restoreStdout();
|
|
983
|
-
restoreStderr();
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
function patchPodStatusWriter(stream) {
|
|
987
|
-
const originalWrite = stream.write.bind(stream);
|
|
988
|
-
stream.write = function patchedWrite(chunk, encodingOrCallback, callback) {
|
|
989
|
-
const encoding = typeof encodingOrCallback === 'string' ? encodingOrCallback : undefined;
|
|
990
|
-
const onComplete = typeof encodingOrCallback === 'function' ? encodingOrCallback : callback;
|
|
991
|
-
const text = typeof chunk === 'string' ? chunk : Buffer.from(chunk).toString(encoding);
|
|
992
|
-
const filtered = stripPodStatusLines(text);
|
|
993
|
-
if (!filtered) {
|
|
994
|
-
onComplete?.();
|
|
995
|
-
return true;
|
|
996
|
-
}
|
|
997
|
-
if (typeof chunk === 'string') {
|
|
998
|
-
return originalWrite(filtered, encodingOrCallback, callback);
|
|
999
|
-
}
|
|
1000
|
-
return originalWrite(Buffer.from(filtered, encoding), callback);
|
|
1001
|
-
};
|
|
1002
|
-
return () => {
|
|
1003
|
-
;
|
|
1004
|
-
stream.write = originalWrite;
|
|
1005
|
-
};
|
|
1006
|
-
}
|
|
1007
|
-
function stripPodStatusLines(input) {
|
|
1008
|
-
const urlPattern = String.raw `https?:\/\/[A-Za-z0-9._~:/?#[\]@!$&'()*+,;=%-]+`;
|
|
1009
|
-
return input
|
|
1010
|
-
.replace(new RegExp(String.raw `\[Container\]\s*容器已存在:\s*${urlPattern}[ \t]*(?:\r?\n)?`, 'g'), '')
|
|
1011
|
-
.replace(new RegExp(String.raw `Connecting to Solid Pod:\s*${urlPattern}[ \t]*(?:\r?\n)?`, 'g'), '')
|
|
1012
|
-
.replace(new RegExp(String.raw `Using WebID:\s*${urlPattern}[ \t]*(?:\r?\n)?`, 'g'), '')
|
|
1013
|
-
.replace(/Successfully connected to Solid Pod[ \t]*(?:\r?\n)?/g, '');
|
|
1144
|
+
return 'LinX Cloud';
|
|
1014
1145
|
}
|
|
1015
1146
|
function isRecord(value) {
|
|
1016
1147
|
return typeof value === 'object' && value !== null;
|
|
1017
1148
|
}
|
|
1149
|
+
function normalizeNonEmptyString(value) {
|
|
1150
|
+
if (typeof value !== 'string') {
|
|
1151
|
+
return undefined;
|
|
1152
|
+
}
|
|
1153
|
+
const normalized = value.trim();
|
|
1154
|
+
return normalized || undefined;
|
|
1155
|
+
}
|
|
1018
1156
|
function stripAnsi(text) {
|
|
1019
1157
|
return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '');
|
|
1020
1158
|
}
|