@undefineds.co/linx 0.2.15 → 0.2.17

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.
Files changed (103) hide show
  1. package/dist/generated/version.js +3 -0
  2. package/dist/generated/version.js.map +1 -0
  3. package/dist/index.js +51 -225
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/account-api.js +1 -1
  6. package/dist/lib/account-api.js.map +1 -1
  7. package/dist/lib/account-session.js +1 -1
  8. package/dist/lib/account-session.js.map +1 -1
  9. package/dist/lib/ai-command.js +59 -32
  10. package/dist/lib/ai-command.js.map +1 -1
  11. package/dist/lib/chat-api.js +19 -177
  12. package/dist/lib/chat-api.js.map +1 -1
  13. package/dist/lib/codex-plugin/bridge.js.map +1 -1
  14. package/dist/lib/codex-plugin/codex-native-proxy.js.map +1 -1
  15. package/dist/lib/codex-plugin/index.js.map +1 -1
  16. package/dist/lib/codex-plugin/runner.js.map +1 -1
  17. package/dist/lib/credentials-store.js +1 -7
  18. package/dist/lib/credentials-store.js.map +1 -1
  19. package/dist/lib/default-model.js +0 -1
  20. package/dist/lib/default-model.js.map +1 -1
  21. package/dist/lib/login-command.js +2 -17
  22. package/dist/lib/login-command.js.map +1 -1
  23. package/dist/lib/models.js +27 -2
  24. package/dist/lib/models.js.map +1 -1
  25. package/dist/lib/oidc-auth.js +13 -78
  26. package/dist/lib/oidc-auth.js.map +1 -1
  27. package/dist/lib/oidc-session-storage.js.map +1 -1
  28. package/dist/lib/pi-adapter/auth.js +5 -12
  29. package/dist/lib/pi-adapter/auth.js.map +1 -1
  30. package/dist/lib/pi-adapter/branding.js +75 -553
  31. package/dist/lib/pi-adapter/branding.js.map +1 -1
  32. package/dist/lib/pi-adapter/index.js.map +1 -1
  33. package/dist/lib/pi-adapter/interactive.js +4 -154
  34. package/dist/lib/pi-adapter/interactive.js.map +1 -1
  35. package/dist/lib/pi-adapter/runtime.js +23 -138
  36. package/dist/lib/pi-adapter/runtime.js.map +1 -1
  37. package/dist/lib/pi-adapter/stream.js +4 -154
  38. package/dist/lib/pi-adapter/stream.js.map +1 -1
  39. package/dist/lib/pi-adapter/theme.js.map +1 -1
  40. package/dist/lib/pod-chat-store.js +1 -1
  41. package/dist/lib/pod-chat-store.js.map +1 -1
  42. package/dist/lib/profile-identity.js +60 -16
  43. package/dist/lib/profile-identity.js.map +1 -1
  44. package/dist/lib/prompt.js.map +1 -1
  45. package/dist/lib/runtime-target.js +1 -1
  46. package/dist/lib/runtime-target.js.map +1 -1
  47. package/dist/lib/solid-auth.js.map +1 -1
  48. package/dist/lib/thread-utils.js.map +1 -1
  49. package/dist/lib/watch/archive.js +1 -1
  50. package/dist/lib/watch/archive.js.map +1 -1
  51. package/dist/lib/watch/auth.js +1 -1
  52. package/dist/lib/watch/auth.js.map +1 -1
  53. package/dist/lib/watch/codex-composer.js.map +1 -1
  54. package/dist/lib/watch/codex-footer.js.map +1 -1
  55. package/dist/lib/watch/codex-overlay.js.map +1 -1
  56. package/dist/lib/watch/codex-request-form.js +1 -1
  57. package/dist/lib/watch/codex-request-form.js.map +1 -1
  58. package/dist/lib/watch/codex-request-input.js.map +1 -1
  59. package/dist/lib/watch/display.js.map +1 -1
  60. package/dist/lib/watch/format.js.map +1 -1
  61. package/dist/lib/watch/hooks/claude.js.map +1 -1
  62. package/dist/lib/watch/hooks/codebuddy.js.map +1 -1
  63. package/dist/lib/watch/hooks/codex.js.map +1 -1
  64. package/dist/lib/watch/hooks/index.js.map +1 -1
  65. package/dist/lib/watch/hooks/shared.js +1 -0
  66. package/dist/lib/watch/hooks/shared.js.map +1 -1
  67. package/dist/lib/watch/index.js.map +1 -1
  68. package/dist/lib/watch/pod-ai.js +32 -16
  69. package/dist/lib/watch/pod-ai.js.map +1 -1
  70. package/dist/lib/watch/pod-approval.js +203 -481
  71. package/dist/lib/watch/pod-approval.js.map +1 -1
  72. package/dist/lib/watch/pod-persistence.js +37 -24
  73. package/dist/lib/watch/pod-persistence.js.map +1 -1
  74. package/dist/lib/watch/runner.js +1 -4
  75. package/dist/lib/watch/runner.js.map +1 -1
  76. package/dist/lib/watch/types.js.map +1 -1
  77. package/dist/skills/drizzle-solid/SKILL.md +340 -0
  78. package/dist/skills/pod-storage/SKILL.md +60 -0
  79. package/dist/skills/solid-modeling/SKILL.md +274 -0
  80. package/dist/skills/xpod-componentsjs/SKILL.md +284 -0
  81. package/dist/watch-cli.js.map +1 -1
  82. package/package.json +10 -3
  83. package/vendor/client/dist/client/index.d.ts +118 -0
  84. package/vendor/client/dist/client/index.js +260 -0
  85. package/vendor/client/dist/index.d.ts +1 -0
  86. package/vendor/client/dist/index.js +1 -0
  87. package/vendor/client/dist/watch/index.d.ts +226 -0
  88. package/vendor/client/dist/watch/index.js +1114 -0
  89. package/vendor/client/package.json +9 -0
  90. package/dist/lib/node-warning-filter.js +0 -34
  91. package/dist/lib/node-warning-filter.js.map +0 -1
  92. package/dist/lib/pi-adapter/pod-approval.js +0 -8
  93. package/dist/lib/pi-adapter/pod-approval.js.map +0 -1
  94. package/dist/lib/pi-adapter/pod-mirror-mapping.js +0 -189
  95. package/dist/lib/pi-adapter/pod-mirror-mapping.js.map +0 -1
  96. package/dist/lib/pi-adapter/pod-mirror.js +0 -334
  97. package/dist/lib/pi-adapter/pod-mirror.js.map +0 -1
  98. package/dist/lib/pi-adapter/pod-native.js +0 -477
  99. package/dist/lib/pi-adapter/pod-native.js.map +0 -1
  100. package/dist/lib/pi-adapter/session.js +0 -727
  101. package/dist/lib/pi-adapter/session.js.map +0 -1
  102. package/dist/lib/pod-data-session.js +0 -70
  103. package/dist/lib/pod-data-session.js.map +0 -1
@@ -1,45 +1,24 @@
1
1
  import { spawn } from 'node:child_process';
2
2
  import { homedir } from 'node:os';
3
3
  import { basename, join } from 'node:path';
4
- import { readFileSync } from 'node:fs';
5
4
  import { LINX_HOME_DIRNAME } from '@undefineds.co/models/client';
6
- import { keyHint, LoginDialogComponent, rawKeyHint } from '@mariozechner/pi-coding-agent';
5
+ import { keyHint, rawKeyHint } from '@mariozechner/pi-coding-agent';
7
6
  import { Text, truncateToWidth, visibleWidth, wrapTextWithAnsi } from '@mariozechner/pi-tui';
8
7
  import { loadCredentials } from '../credentials-store.js';
9
8
  import { extractUsernameFromWebId, resolveProfileDisplayName } from '../profile-identity.js';
9
+ import { LINX_CLI_VERSION } from '../../generated/version.js';
10
10
  export const LINX_AGENT_DIR = join(homedir(), LINX_HOME_DIRNAME, 'agent');
11
11
  export const LINX_UPDATE_PACKAGE_NAME = '@undefineds.co/linx';
12
12
  export const LINX_CHANGELOG_URL = 'https://github.com/undefineds-co/linx-cli/releases';
13
- export const LINX_CLI_VERSION = readLinxCliVersion();
14
- const LINX_AUTH_LOGIN_IN_PROGRESS = Symbol.for('linx.tui.authLoginInProgress');
15
- const LINX_AUTH_LOGIN_ON_INIT = Symbol.for('linx.tui.authLoginOnInit');
16
- const LINX_AUTH_LOGIN_SCHEDULED = Symbol.for('linx.tui.authLoginScheduled');
17
- const LINX_AUTH_REPORTING_ERROR = Symbol.for('linx.tui.authReportingError');
18
13
  const LINX_UPDATE_IN_PROGRESS = Symbol.for('linx.tui.updateInProgress');
19
- const LINX_PROVIDER_ID = 'undefineds';
20
- const AUTH_OPTION_BROWSER = 'Authorize in browser';
21
- const AUTH_OPTION_API_KEY = 'Enter API key';
22
- const AUTH_OPTION_EXIT = 'Exit';
23
- const UPDATE_OPTION_INSTALL = 'Install update and restart';
24
- const UPDATE_OPTION_CHANGELOG = 'Open changelog';
25
- const UPDATE_OPTION_LATER = 'Later';
14
+ const LINX_UPDATE_RUNNER = Symbol.for('linx.tui.updateRunner');
26
15
  export function applyLinxInteractiveBranding(interactive) {
27
16
  patchTerminalTitle(interactive);
28
17
  patchVersionCheck(interactive);
29
18
  patchUpdateNotification(interactive);
30
- patchNativeOAuthSelectors(interactive);
31
- patchLoginCommand(interactive);
32
- patchAuthExpiredSessionEvents(interactive);
33
- patchAuthExpiredLoginPrompt(interactive);
19
+ patchUpdateCommand(interactive);
34
20
  patchHeader(interactive);
35
21
  }
36
- export function requestLinxCloudLogin(interactive, reason = 'manual') {
37
- if (!interactive.isInitialized) {
38
- interactive[LINX_AUTH_LOGIN_ON_INIT] = reason;
39
- return;
40
- }
41
- void startLinxCloudLogin(interactive, { reason });
42
- }
43
22
  function patchTerminalTitle(interactive) {
44
23
  const original = interactive.updateTerminalTitle?.bind(interactive);
45
24
  interactive.updateTerminalTitle = function patchedUpdateTerminalTitle() {
@@ -64,7 +43,7 @@ function patchVersionCheck(interactive) {
64
43
  }
65
44
  const body = await response.json();
66
45
  const latest = typeof body.version === 'string' ? body.version.trim() : '';
67
- if (!latest || !isVersionNewer(latest, LINX_CLI_VERSION)) {
46
+ if (!latest || !isNewerVersion(latest, LINX_CLI_VERSION)) {
68
47
  return undefined;
69
48
  }
70
49
  return latest;
@@ -76,109 +55,45 @@ function patchVersionCheck(interactive) {
76
55
  }
77
56
  function patchUpdateNotification(interactive) {
78
57
  interactive.showNewVersionNotification = function patchedShowNewVersionNotification(newVersion) {
79
- void showLinxUpdateSelector(this, newVersion);
58
+ const lines = [
59
+ '\x1b[1m\x1b[33mLinX Update Available\x1b[39m\x1b[22m',
60
+ `\x1b[2mNew version ${newVersion} is available. \x1b[22m\x1b[36mType /update to install now.\x1b[39m`,
61
+ `\x1b[2mManual install: \x1b[22m\x1b[36mnpm install -g ${LINX_UPDATE_PACKAGE_NAME}@latest\x1b[39m`,
62
+ `\x1b[2mChangelog: \x1b[22m\x1b[36m${LINX_CHANGELOG_URL}\x1b[39m`,
63
+ ];
64
+ this.chatContainer?.addChild?.(new Text(lines.join('\n'), 1, 0));
65
+ this.ui?.requestRender?.();
80
66
  };
81
67
  }
82
- async function showLinxUpdateSelector(interactive, newVersion) {
83
- if (interactive[LINX_UPDATE_IN_PROGRESS]) {
84
- return;
68
+ function isNewerVersion(candidate, current) {
69
+ const candidateParts = parseSemverCore(candidate);
70
+ const currentParts = parseSemverCore(current);
71
+ if (!candidateParts || !currentParts) {
72
+ return candidate !== current;
85
73
  }
86
- interactive[LINX_UPDATE_IN_PROGRESS] = true;
87
- try {
88
- const title = [
89
- 'LinX update available',
90
- `Current ${LINX_CLI_VERSION} -> latest ${newVersion}`,
91
- 'Choose how to handle this update.',
92
- ].join('\n');
93
- const options = [UPDATE_OPTION_INSTALL, UPDATE_OPTION_CHANGELOG, UPDATE_OPTION_LATER];
94
- const selected = typeof interactive.showExtensionSelector === 'function'
95
- ? await interactive.showExtensionSelector(title, options)
96
- : undefined;
97
- if (selected === UPDATE_OPTION_INSTALL) {
98
- await installLinxUpdateAndRestart(interactive, newVersion);
99
- return;
100
- }
101
- if (selected === UPDATE_OPTION_CHANGELOG) {
102
- openExternalUrl(LINX_CHANGELOG_URL, interactive);
103
- interactive.showStatus?.(`Opened LinX changelog for ${newVersion}.`);
104
- return;
74
+ for (let index = 0; index < 3; index += 1) {
75
+ if (candidateParts[index] > currentParts[index]) {
76
+ return true;
105
77
  }
106
- if (!selected) {
107
- showLinxUpdateFallback(interactive, newVersion);
108
- return;
78
+ if (candidateParts[index] < currentParts[index]) {
79
+ return false;
109
80
  }
110
- interactive.showStatus?.(`Skipped LinX ${newVersion} for now.`);
111
- }
112
- finally {
113
- interactive[LINX_UPDATE_IN_PROGRESS] = false;
114
81
  }
82
+ return false;
115
83
  }
116
- async function installLinxUpdateAndRestart(interactive, newVersion) {
117
- interactive.showStatus?.(`Installing LinX ${newVersion}...`);
118
- interactive.ui?.requestRender?.();
119
- try {
120
- await runNpmInstallLatest();
121
- }
122
- catch (error) {
123
- const message = error instanceof Error ? error.message : String(error);
124
- interactive.showError?.(`LinX update failed: ${message}`);
125
- return;
84
+ function parseSemverCore(version) {
85
+ const match = version.trim().match(/^(\d+)\.(\d+)\.(\d+)(?:[-+].*)?$/);
86
+ if (!match) {
87
+ return null;
126
88
  }
127
- interactive.showStatus?.(`LinX ${newVersion} installed. Restarting...`);
128
- interactive.ui?.requestRender?.();
129
- restartCurrentProcess(interactive);
130
- }
131
- function runNpmInstallLatest() {
132
- const npmCommand = process.env.npm_execpath || 'npm';
133
- const args = ['install', '-g', `${LINX_UPDATE_PACKAGE_NAME}@latest`];
134
- return new Promise((resolve, reject) => {
135
- const child = spawn(npmCommand, args, {
136
- stdio: ['ignore', 'pipe', 'pipe'],
137
- shell: false,
138
- });
139
- let stderr = '';
140
- child.stderr?.on('data', (chunk) => {
141
- stderr += chunk.toString();
142
- });
143
- child.on('error', reject);
144
- child.on('close', (code) => {
145
- if (code === 0) {
146
- resolve();
147
- return;
148
- }
149
- reject(new Error(stderr.trim() || `npm install exited with code ${code ?? 'unknown'}`));
150
- });
151
- });
152
- }
153
- function restartCurrentProcess(interactive) {
154
- const child = spawn(process.execPath, process.argv.slice(1), {
155
- cwd: process.cwd(),
156
- env: process.env,
157
- stdio: 'inherit',
158
- detached: false,
159
- });
160
- child.on('error', (error) => {
161
- interactive.showError?.(`LinX restart failed: ${error.message}`);
162
- });
163
- interactive.stop?.();
164
- setTimeout(() => process.exit(0), 50);
165
- }
166
- function showLinxUpdateFallback(interactive, newVersion) {
167
- const lines = [
168
- '\x1b[1m\x1b[33mLinX update available\x1b[39m\x1b[22m',
169
- `\x1b[2mCurrent ${LINX_CLI_VERSION} -> latest ${newVersion}\x1b[22m`,
170
- `\x1b[2mRun \x1b[22m\x1b[36mnpm install -g ${LINX_UPDATE_PACKAGE_NAME}@latest\x1b[39m\x1b[2m if this terminal cannot show the update selector.\x1b[22m`,
171
- `\x1b[2mChangelog: \x1b[22m\x1b[36m${LINX_CHANGELOG_URL}\x1b[39m`,
172
- ];
173
- interactive.chatContainer?.addChild?.(new Text(lines.join('\n'), 1, 0));
174
- interactive.ui?.requestRender?.();
89
+ return [Number(match[1]), Number(match[2]), Number(match[3])];
175
90
  }
176
- function patchLoginCommand(interactive) {
91
+ function patchUpdateCommand(interactive) {
177
92
  const originalSetup = interactive.setupEditorSubmitHandler?.bind(interactive);
178
93
  if (typeof originalSetup !== 'function') {
179
94
  return;
180
95
  }
181
- interactive.setupEditorSubmitHandler = function patchedLinxLoginSetupEditorSubmitHandler() {
96
+ interactive.setupEditorSubmitHandler = function patchedSetupEditorSubmitHandler() {
182
97
  originalSetup();
183
98
  const originalSubmit = this.defaultEditor?.onSubmit?.bind(this.defaultEditor);
184
99
  if (typeof originalSubmit !== 'function') {
@@ -186,426 +101,77 @@ function patchLoginCommand(interactive) {
186
101
  }
187
102
  this.defaultEditor.onSubmit = async (text) => {
188
103
  const command = text.trim();
189
- if (command === '/login') {
104
+ if (command === '/update' || command === '/update linx') {
190
105
  this.editor?.setText?.('');
191
- await startLinxCloudLogin(this);
106
+ await runLinxUpdateFromTui(this);
192
107
  return;
193
108
  }
194
109
  await originalSubmit(text);
195
110
  };
196
111
  };
197
112
  }
198
- function patchNativeOAuthSelectors(interactive) {
199
- interactive.showOAuthSelector = async function patchedLinxOAuthSelector(mode = 'login') {
200
- if (mode === 'logout') {
201
- const authStorage = this.session?.modelRegistry?.authStorage;
202
- authStorage?.logout?.(LINX_PROVIDER_ID);
203
- authStorage?.setRuntimeApiKey?.(LINX_PROVIDER_ID, '');
204
- await refreshLinxAuthState(this);
205
- this.showStatus?.('Logged out of LinX Cloud.');
206
- return;
207
- }
208
- await startLinxCloudLogin(this, { reason: 'manual' });
209
- };
210
- interactive.showLoginDialog = async function patchedLinxLoginDialog(providerId) {
211
- if (!providerId || providerId === LINX_PROVIDER_ID) {
212
- await startLinxCloudLogin(this, { reason: 'manual' });
213
- return;
214
- }
215
- this.showStatus?.('LinX only supports LinX Cloud authentication in this TUI.');
216
- await startLinxCloudLogin(this, { reason: 'manual' });
217
- };
218
- }
219
- function patchAuthExpiredSessionEvents(interactive) {
220
- const originalHandleEvent = interactive.handleEvent?.bind(interactive);
221
- if (typeof originalHandleEvent !== 'function') {
222
- return;
223
- }
224
- interactive.handleEvent = async function patchedHandleEvent(event) {
225
- const result = await originalHandleEvent(event);
226
- if (eventHasLinxAuthExpiredError(event)) {
227
- scheduleLinxCloudLogin(this, 'expired');
228
- }
229
- return result;
230
- };
231
- }
232
- function patchAuthExpiredLoginPrompt(interactive) {
233
- const originalShowError = interactive.showError?.bind(interactive);
234
- if (typeof originalShowError !== 'function') {
235
- return;
236
- }
237
- interactive.showError = function patchedShowError(errorMessage) {
238
- const text = typeof errorMessage === 'string' ? errorMessage : String(errorMessage);
239
- if (!isLinxAuthExpiredError(text)) {
240
- return originalShowError(errorMessage);
241
- }
242
- scheduleLinxCloudLogin(this, 'expired');
243
- return undefined;
244
- };
245
- }
246
- function isLinxAuthExpiredError(text) {
247
- const normalized = stripAnsi(text).toLowerCase();
248
- return normalized.includes('linx cloud login expired')
249
- || normalized.includes('invalid solid token')
250
- || (normalized.includes('chat request failed (401)') && normalized.includes('unauthorized'));
251
- }
252
- function eventHasLinxAuthExpiredError(event) {
253
- if (!isRecord(event)) {
254
- return false;
255
- }
256
- const message = isRecord(event.message) ? event.message : undefined;
257
- const errorMessage = typeof message?.errorMessage === 'string' ? message.errorMessage : '';
258
- const error = isRecord(event.error) ? event.error : undefined;
259
- const nestedErrorMessage = typeof error?.errorMessage === 'string' ? error.errorMessage : '';
260
- return isLinxAuthExpiredError(`${errorMessage}\n${nestedErrorMessage}`);
261
- }
262
- async function startLinxCloudLogin(interactive, options = {}) {
263
- if (interactive[LINX_AUTH_LOGIN_IN_PROGRESS]) {
113
+ async function runLinxUpdateFromTui(interactive) {
114
+ if (interactive[LINX_UPDATE_IN_PROGRESS]) {
115
+ interactive.showStatus?.('LinX update is already running');
264
116
  return;
265
117
  }
266
- interactive[LINX_AUTH_LOGIN_IN_PROGRESS] = true;
118
+ interactive[LINX_UPDATE_IN_PROGRESS] = true;
119
+ const packageSpec = `${LINX_UPDATE_PACKAGE_NAME}@latest`;
120
+ const npmCommand = process.env.npm_execpath && !process.env.npm_execpath.endsWith('npx-cli.js')
121
+ ? process.execPath
122
+ : 'npm';
123
+ const args = npmCommand === process.execPath
124
+ ? [process.env.npm_execpath, 'install', '-g', packageSpec]
125
+ : ['install', '-g', packageSpec];
267
126
  try {
268
- const authStorage = interactive.session?.modelRegistry?.authStorage;
269
- if (!authStorage) {
270
- prefillLoginCommand(interactive);
271
- return;
272
- }
273
- const reason = options.reason ?? 'manual';
274
- const selected = await selectLinxAuthMethod(interactive, reason);
275
- if (!selected) {
276
- interactive.showStatus?.('LinX Cloud authorization cancelled.');
277
- return;
278
- }
279
- if (selected === AUTH_OPTION_BROWSER) {
280
- if (typeof authStorage.login !== 'function') {
281
- prefillLoginCommand(interactive);
282
- return;
283
- }
284
- await runLinxCloudBrowserLogin(interactive, authStorage, reason);
285
- await refreshLinxAuthState(interactive);
286
- interactive.showStatus?.(`${authStatusPrefix(reason)} Browser authorization complete. Retry your message.`);
287
- return;
288
- }
289
- if (selected === AUTH_OPTION_API_KEY) {
290
- await promptForLinxApiKey(interactive, reason);
127
+ interactive.showStatus?.(`Installing LinX update: ${packageSpec}`);
128
+ interactive.ui?.requestRender?.();
129
+ interactive.ui?.stop?.();
130
+ const runner = resolveUpdateRunner(interactive);
131
+ const result = await runner(npmCommand, args);
132
+ interactive.ui?.start?.();
133
+ if (result.exitCode === 0) {
134
+ interactive.showStatus?.('LinX updated. Restart linx to use the new version.');
291
135
  return;
292
136
  }
293
- if (selected === AUTH_OPTION_EXIT) {
294
- if (reason === 'startup') {
295
- interactive.stop?.();
296
- }
297
- else {
298
- interactive.showStatus?.('LinX Cloud authorization cancelled.');
299
- }
300
- }
137
+ const detail = result.signal ? `signal ${result.signal}` : `exit code ${result.exitCode}`;
138
+ interactive.showError?.(`LinX update failed with ${detail}`);
301
139
  }
302
140
  catch (error) {
141
+ interactive.ui?.start?.();
303
142
  const message = error instanceof Error ? error.message : String(error);
304
- reportLinxLoginError(interactive, message);
305
- }
306
- finally {
307
- interactive[LINX_AUTH_LOGIN_IN_PROGRESS] = false;
308
- }
309
- }
310
- function scheduleLinxCloudLogin(interactive, reason) {
311
- if (interactive[LINX_AUTH_LOGIN_IN_PROGRESS] || interactive[LINX_AUTH_LOGIN_SCHEDULED]) {
312
- return;
313
- }
314
- interactive[LINX_AUTH_LOGIN_SCHEDULED] = true;
315
- setTimeout(() => {
316
- interactive[LINX_AUTH_LOGIN_SCHEDULED] = false;
317
- void startLinxCloudLogin(interactive, { reason });
318
- }, 0);
319
- }
320
- function reportLinxLoginError(interactive, message) {
321
- const rendered = normalizeLinxLoginError(message);
322
- if (interactive[LINX_AUTH_REPORTING_ERROR]) {
323
- interactive.showStatus?.(rendered);
324
- return;
325
- }
326
- interactive[LINX_AUTH_REPORTING_ERROR] = true;
327
- try {
328
- if (typeof interactive.showError === 'function') {
329
- interactive.showError(rendered);
330
- }
331
- else {
332
- interactive.showStatus?.(rendered);
333
- }
143
+ interactive.showError?.(`LinX update failed: ${message}`);
334
144
  }
335
145
  finally {
336
- interactive[LINX_AUTH_REPORTING_ERROR] = false;
337
- }
338
- }
339
- function normalizeLinxLoginError(message) {
340
- const oidcCallbackError = /^OIDC callback returned\b/i.test(message);
341
- if (oidcCallbackError) {
342
- return `LinX Cloud login failed: ${message}`;
343
- }
344
- if (/server_error/i.test(message)) {
345
- return `LinX Cloud login failed: the identity server rejected this browser login. ${message}`;
346
- }
347
- return `LinX Cloud login failed: ${message}`;
348
- }
349
- async function selectLinxAuthMethod(interactive, reason) {
350
- const title = buildLinxAuthPromptTitle(reason, resolveRuntimeProviderLabel(interactive));
351
- const options = [AUTH_OPTION_BROWSER, AUTH_OPTION_API_KEY, AUTH_OPTION_EXIT];
352
- if (typeof interactive.showExtensionSelector === 'function') {
353
- return await interactive.showExtensionSelector(title, options);
354
- }
355
- showLinxAuthFallback(interactive, title, options);
356
- return undefined;
357
- }
358
- function buildLinxAuthPromptTitle(reason, providerLabel) {
359
- if (reason === 'startup') {
360
- return [
361
- 'LinX Cloud login required',
362
- `Connect to ${providerLabel} before using LinX TUI.`,
363
- 'Choose a sign-in method.',
364
- ].join('\n');
365
- }
366
- if (reason === 'expired') {
367
- return [
368
- 'LinX Cloud login expired',
369
- 'Your current Solid token was rejected by LinX Cloud.',
370
- 'Re-authorize or provide an API key, then retry your message.',
371
- ].join('\n');
372
- }
373
- return [
374
- 'LinX Cloud authorization',
375
- `Choose how LinX should authenticate with ${providerLabel}.`,
376
- ].join('\n');
377
- }
378
- function showLinxAuthFallback(interactive, title, options) {
379
- interactive.chatContainer?.addChild?.(new Text([
380
- `\x1b[1m${title}\x1b[22m`,
381
- '',
382
- ...options.map((option) => `- ${option}`),
383
- '',
384
- 'This terminal build cannot render the LinX auth selector. Run `linx login` in another shell.',
385
- ].join('\n'), 1, 0));
386
- interactive.ui?.requestRender?.();
387
- }
388
- async function promptForLinxApiKey(interactive, reason) {
389
- if (typeof interactive.showExtensionInput !== 'function') {
390
- interactive.showError?.('This terminal build cannot collect an API key inside the TUI.');
391
- return;
392
- }
393
- const apiKey = await interactive.showExtensionInput([
394
- reason === 'expired' ? 'Enter LinX Cloud API key' : 'Use LinX Cloud API key',
395
- 'Paste a key for this TUI session. Press Escape to cancel.',
396
- ].join('\n'), 'linx API key');
397
- const trimmed = typeof apiKey === 'string' ? apiKey.trim() : '';
398
- if (!trimmed) {
399
- interactive.showStatus?.('LinX Cloud API key entry cancelled.');
400
- return;
401
- }
402
- const authStorage = interactive.session?.modelRegistry?.authStorage;
403
- authStorage?.setRuntimeApiKey?.(LINX_PROVIDER_ID, trimmed);
404
- authStorage?.set?.(LINX_PROVIDER_ID, { type: 'api_key', key: trimmed });
405
- await refreshLinxAuthState(interactive);
406
- interactive.showStatus?.(`${authStatusPrefix(reason)} API key saved for this TUI session. Retry your message.`);
407
- }
408
- async function refreshLinxAuthState(interactive) {
409
- interactive.session?.modelRegistry?.refresh?.();
410
- await interactive.updateAvailableProviderCount?.();
411
- interactive.ui?.requestRender?.();
412
- }
413
- function authStatusPrefix(reason) {
414
- if (reason === 'expired') {
415
- return 'LinX Cloud login refreshed.';
416
- }
417
- if (reason === 'startup') {
418
- return 'LinX Cloud connected.';
419
- }
420
- return 'LinX Cloud authorization updated.';
421
- }
422
- async function runLinxCloudLogin(interactive, authStorage, reason) {
423
- await authStorage.login(LINX_PROVIDER_ID, {
424
- forceFresh: reason === 'expired',
425
- onAuth(info) {
426
- showLinxLoginUrl(interactive, info);
427
- openLoginUrl(info.url, interactive);
428
- if (info.instructions) {
429
- interactive.showStatus?.(info.instructions);
430
- }
431
- },
432
- onProgress(message) {
433
- interactive.showStatus?.(message);
434
- interactive.ui?.requestRender?.();
435
- },
436
- onManualCodeInput(signal) {
437
- return promptForLinxManualRedirectUrl(interactive, signal);
438
- },
439
- });
440
- syncRuntimeCredential(interactive);
441
- }
442
- async function runLinxCloudBrowserLogin(interactive, authStorage, reason) {
443
- if (canRenderLinxLoginDialog(interactive)) {
444
- await runLinxCloudLoginDialog(interactive, authStorage, reason);
445
- return;
446
- }
447
- await runLinxCloudLogin(interactive, authStorage, reason);
448
- }
449
- function canRenderLinxLoginDialog(interactive) {
450
- return Boolean(interactive.ui
451
- && typeof interactive.editorContainer?.clear === 'function'
452
- && typeof interactive.editorContainer?.addChild === 'function'
453
- && interactive.editor);
454
- }
455
- async function runLinxCloudLoginDialog(interactive, authStorage, reason) {
456
- const dialog = new LoginDialogComponent(interactive.ui, LINX_PROVIDER_ID, () => undefined);
457
- const restoreEditor = () => {
458
- interactive.editorContainer.clear();
459
- interactive.editorContainer.addChild(interactive.editor);
460
- interactive.ui?.setFocus?.(interactive.editor);
146
+ interactive[LINX_UPDATE_IN_PROGRESS] = false;
461
147
  interactive.ui?.requestRender?.();
462
- };
463
- interactive.editorContainer.clear();
464
- interactive.editorContainer.addChild(dialog);
465
- interactive.ui?.setFocus?.(dialog);
466
- interactive.ui?.requestRender?.();
467
- let manualRedirectResolve;
468
- let manualRedirectReject;
469
- const manualRedirectPromise = new Promise((resolve, reject) => {
470
- manualRedirectResolve = resolve;
471
- manualRedirectReject = reject;
472
- });
473
- try {
474
- await authStorage.login(LINX_PROVIDER_ID, {
475
- forceFresh: reason === 'expired',
476
- onAuth(info) {
477
- dialog.showAuth(info.url, info.instructions);
478
- dialog.showManualInput('Paste redirect URL below, or complete login in browser:')
479
- .then((value) => {
480
- if (value && manualRedirectResolve) {
481
- manualRedirectResolve(value);
482
- manualRedirectResolve = undefined;
483
- manualRedirectReject = undefined;
484
- }
485
- })
486
- .catch((error) => {
487
- if (manualRedirectReject) {
488
- manualRedirectReject(error);
489
- manualRedirectResolve = undefined;
490
- manualRedirectReject = undefined;
491
- }
492
- });
493
- },
494
- onProgress(message) {
495
- dialog.showProgress(message);
496
- },
497
- onManualCodeInput(signal) {
498
- return waitForLinxDialogManualRedirect(manualRedirectPromise, signal);
499
- },
500
- signal: dialog.signal,
501
- });
502
- syncRuntimeCredential(interactive);
503
- }
504
- finally {
505
- restoreEditor();
506
148
  }
507
149
  }
508
- function waitForLinxDialogManualRedirect(manualRedirectPromise, signal) {
509
- if (!signal) {
510
- return manualRedirectPromise;
511
- }
512
- if (signal.aborted) {
513
- return Promise.resolve('');
514
- }
150
+ function resolveUpdateRunner(interactive) {
151
+ return typeof interactive[LINX_UPDATE_RUNNER] === 'function'
152
+ ? interactive[LINX_UPDATE_RUNNER]
153
+ : spawnInstall;
154
+ }
155
+ function spawnInstall(command, args) {
515
156
  return new Promise((resolve, reject) => {
516
- const onAbort = () => resolve('');
517
- signal.addEventListener('abort', onAbort, { once: true });
518
- manualRedirectPromise
519
- .then((value) => {
520
- signal.removeEventListener('abort', onAbort);
521
- resolve(value);
522
- })
523
- .catch((error) => {
524
- signal.removeEventListener('abort', onAbort);
525
- reject(error);
157
+ const child = spawn(command, args, {
158
+ stdio: 'inherit',
159
+ shell: process.platform === 'win32',
526
160
  });
161
+ child.once('error', reject);
162
+ child.once('close', (exitCode, signal) => resolve({ exitCode, signal }));
527
163
  });
528
164
  }
529
- async function promptForLinxManualRedirectUrl(interactive, signal) {
530
- if (typeof interactive.showExtensionInput !== 'function') {
531
- throw new Error('Manual redirect paste is not available in this terminal. Run `linx login` in another shell if the browser callback is blocked.');
532
- }
533
- const redirect = await interactive.showExtensionInput([
534
- 'Paste final redirect URL',
535
- 'If the browser cannot return to this terminal, paste the full callback URL below.',
536
- ].join('\n'), 'http://127.0.0.1:PORT/auth/callback?code=...&state=...&iss=...', signal ? { signal } : undefined);
537
- const trimmed = typeof redirect === 'string' ? redirect.trim() : '';
538
- if (!trimmed) {
539
- throw new Error('Login cancelled');
540
- }
541
- return trimmed;
542
- }
543
- function syncRuntimeCredential(interactive) {
544
- const authStorage = interactive.session?.modelRegistry?.authStorage;
545
- const credential = authStorage?.get?.(LINX_PROVIDER_ID);
546
- if (credential?.type === 'oauth' && typeof credential.access === 'string' && credential.access) {
547
- authStorage.setRuntimeApiKey?.(LINX_PROVIDER_ID, credential.access);
548
- return;
549
- }
550
- if (credential?.type === 'api_key' && typeof credential.key === 'string' && credential.key) {
551
- authStorage.setRuntimeApiKey?.(LINX_PROVIDER_ID, credential.key);
552
- }
553
- }
554
- function showLinxLoginUrl(interactive, info) {
555
- const lines = [
556
- '\x1b[1mLinX Cloud authorization\x1b[22m',
557
- 'Complete consent in the browser, then return here.',
558
- '',
559
- `\x1b[36m${info.url}\x1b[39m`,
560
- ];
561
- if (info.instructions) {
562
- lines.push('', `\x1b[2m${info.instructions}\x1b[22m`);
563
- }
564
- interactive.chatContainer?.addChild?.(new Text(lines.join('\n'), 1, 0));
565
- interactive.ui?.requestRender?.();
566
- }
567
- function openLoginUrl(url, interactive) {
568
- openExternalUrl(url, interactive);
569
- }
570
- function openExternalUrl(url, interactive) {
571
- if (typeof interactive.openExternal === 'function') {
572
- interactive.openExternal(url);
573
- return;
574
- }
575
- const command = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open';
576
- const args = process.platform === 'win32' ? ['/c', 'start', '', url] : [url];
577
- const child = spawn(command, args, {
578
- detached: true,
579
- stdio: 'ignore',
580
- shell: false,
581
- });
582
- child.unref();
583
- }
584
- function prefillLoginCommand(interactive) {
585
- interactive.editor?.setText?.('/login');
586
- interactive.ui?.setFocus?.(interactive.editor);
587
- interactive.ui?.requestRender?.();
588
- }
589
165
  function patchHeader(interactive) {
590
- const originalInit = interactive.init?.bind(interactive);
591
- if (typeof originalInit !== 'function') {
592
- return;
593
- }
166
+ const originalInit = interactive.init.bind(interactive);
594
167
  interactive.init = async function patchedInit() {
595
168
  await originalInit();
596
- if (this[LINX_AUTH_LOGIN_ON_INIT]) {
597
- const reason = typeof this[LINX_AUTH_LOGIN_ON_INIT] === 'string'
598
- ? this[LINX_AUTH_LOGIN_ON_INIT]
599
- : 'startup';
600
- this[LINX_AUTH_LOGIN_ON_INIT] = false;
601
- queueMicrotask(() => startLinxCloudLogin(this, { reason }));
602
- }
603
169
  const quietStartup = this.options?.verbose ? false : this.settingsManager?.getQuietStartup?.();
604
170
  if (quietStartup) {
605
171
  return;
606
172
  }
607
173
  let profileDisplayName = null;
608
- const replacement = new LinxWelcomeCard(() => buildLinxWelcomeCardState(this, profileDisplayName));
174
+ const replacement = new LinxWelcomeCard(() => buildHeaderState(this, profileDisplayName));
609
175
  const currentHeader = this.customHeader ?? this.builtInHeader;
610
176
  const index = this.headerContainer?.children?.indexOf?.(currentHeader) ?? -1;
611
177
  if (index >= 0) {
@@ -661,13 +227,13 @@ class LinxWelcomeCard {
661
227
  ];
662
228
  }
663
229
  }
664
- export function buildLinxWelcomeCardState(interactive, profileDisplayName = null) {
230
+ function buildHeaderState(interactive, profileDisplayName = null) {
665
231
  const credentials = loadCredentials();
666
232
  const webId = credentials?.webId ?? 'not logged in';
667
233
  const workspace = interactive?.sessionManager?.getCwd?.() || process.cwd();
668
234
  const sessionId = interactive?.sessionManager?.getSessionId?.();
669
235
  const sessionName = interactive?.sessionManager?.getSessionName?.();
670
- const session = sessionName && sessionId ? `${sessionName} (${formatSessionId(sessionId)})` : formatSessionId(sessionId);
236
+ const session = sessionName && sessionId ? `${sessionName} (${shortSessionId(sessionId)})` : shortSessionId(sessionId);
671
237
  const model = interactive?.session?.model?.id ?? 'unknown-model';
672
238
  return {
673
239
  webId,
@@ -680,7 +246,7 @@ export function buildLinxWelcomeCardState(interactive, profileDisplayName = null
680
246
  keyHint('tui.input.submit', 'send'),
681
247
  keyHint('app.model.select', 'model'),
682
248
  rawKeyHint('/login', 'auth'),
683
- rawKeyHint('/hotkeys', 'keymap'),
249
+ rawKeyHint('/help', 'help'),
684
250
  ].join(' \x1b[2m·\x1b[22m '),
685
251
  };
686
252
  }
@@ -698,11 +264,11 @@ function wrapAndPad(line, width) {
698
264
  ? wrapped.map((entry) => padLine(entry, width))
699
265
  : [padLine('', width)];
700
266
  }
701
- function formatSessionId(sessionId) {
267
+ function shortSessionId(sessionId) {
702
268
  if (typeof sessionId !== 'string' || !sessionId.trim()) {
703
269
  return 'new session';
704
270
  }
705
- return sessionId.trim();
271
+ return sessionId.length > 12 ? sessionId.slice(0, 12) : sessionId;
706
272
  }
707
273
  function padLine(line, width) {
708
274
  const visible = visibleWidth(line);
@@ -711,44 +277,6 @@ function padLine(line, width) {
711
277
  }
712
278
  return `${line}${' '.repeat(width - visible)}`;
713
279
  }
714
- function readLinxCliVersion() {
715
- try {
716
- const raw = readFileSync(new URL('../../../package.json', import.meta.url), 'utf-8');
717
- const pkg = JSON.parse(raw);
718
- return typeof pkg.version === 'string' && pkg.version.trim() ? pkg.version.trim() : '0.1.0';
719
- }
720
- catch {
721
- return '0.1.0';
722
- }
723
- }
724
- export function isVersionNewer(candidate, current) {
725
- const candidateVersion = parseSemverLike(candidate);
726
- const currentVersion = parseSemverLike(current);
727
- if (!candidateVersion || !currentVersion) {
728
- return candidate !== current;
729
- }
730
- for (const key of ['major', 'minor', 'patch']) {
731
- if (candidateVersion[key] > currentVersion[key]) {
732
- return true;
733
- }
734
- if (candidateVersion[key] < currentVersion[key]) {
735
- return false;
736
- }
737
- }
738
- return !candidateVersion.prerelease && currentVersion.prerelease;
739
- }
740
- function parseSemverLike(version) {
741
- const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.+)?$/.exec(version.trim());
742
- if (!match) {
743
- return null;
744
- }
745
- return {
746
- major: Number(match[1]),
747
- minor: Number(match[2]),
748
- patch: Number(match[3]),
749
- prerelease: Boolean(match[4]),
750
- };
751
- }
752
280
  function resolveRuntimeProviderLabel(interactive) {
753
281
  const bridge = interactive?.runtimeHost?.linxAuthBridge ?? interactive?.linxAuthBridge;
754
282
  if (bridge?.providerLabel) {
@@ -799,10 +327,4 @@ function stripPodStatusLines(input) {
799
327
  .replace(new RegExp(String.raw `Using WebID:\s*${urlPattern}[ \t]*(?:\r?\n)?`, 'g'), '')
800
328
  .replace(/Successfully connected to Solid Pod[ \t]*(?:\r?\n)?/g, '');
801
329
  }
802
- function isRecord(value) {
803
- return typeof value === 'object' && value !== null;
804
- }
805
- function stripAnsi(text) {
806
- return text.replace(/\x1b\[[0-?]*[ -/]*[@-~]/g, '');
807
- }
808
330
  //# sourceMappingURL=branding.js.map