pi2dsh 0.12.2 → 0.12.4

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 (44) hide show
  1. package/README.md +47 -10
  2. package/README.zh.md +39 -8
  3. package/dist/{analyzer-CEjVQKXA.mjs → analyzer-CXNbeBAe.mjs} +2 -2
  4. package/dist/{analyzer-CEjVQKXA.mjs.map → analyzer-CXNbeBAe.mjs.map} +1 -1
  5. package/dist/cli.mjs +2 -2
  6. package/dist/client.js +525 -0
  7. package/dist/compat/pi-ai.d.mts +36 -1
  8. package/dist/compat/pi-ai.d.mts.map +1 -1
  9. package/dist/compat/pi-ai.mjs +2 -2
  10. package/dist/compat/pi-coding-agent.d.mts +2 -1411
  11. package/dist/compat/pi-coding-agent.mjs +1 -1
  12. package/dist/compat/pi-tui.mjs +1 -1
  13. package/dist/{oauth-bridge-CfyImWx4.mjs → credentials-oauth-BakY-0PA.mjs} +172 -13
  14. package/dist/credentials-oauth-BakY-0PA.mjs.map +1 -0
  15. package/dist/credentials-oauth.mjs +1 -89
  16. package/dist/host.mjs +4 -2
  17. package/dist/host.mjs.map +1 -1
  18. package/dist/index.d.mts +16 -4
  19. package/dist/index.d.mts.map +1 -1
  20. package/dist/index.mjs +3 -3
  21. package/dist/mcp-config-CZepacin.mjs +766 -0
  22. package/dist/mcp-config-CZepacin.mjs.map +1 -0
  23. package/dist/{pi-ai-Dyg4zyLZ.mjs → pi-ai-BA3V_J0V.mjs} +40 -2
  24. package/dist/{pi-ai-Dyg4zyLZ.mjs.map → pi-ai-BA3V_J0V.mjs.map} +1 -1
  25. package/dist/pi-coding-agent-B0PT23gE.d.mts +1412 -0
  26. package/dist/pi-coding-agent-B0PT23gE.d.mts.map +1 -0
  27. package/dist/{pi-coding-agent-CYnmeJEx.mjs → pi-coding-agent-D9skobb6.mjs} +0 -0
  28. package/dist/pi-coding-agent-D9skobb6.mjs.map +1 -0
  29. package/dist/{pi-tui-5CYLcj-_.mjs → pi-tui-DruSeKmd.mjs} +3 -3
  30. package/dist/pi-tui-DruSeKmd.mjs.map +1 -0
  31. package/dist/{runtime-DsPaznXJ.mjs → runtime-DH0zEUpu.mjs} +1075 -67
  32. package/dist/runtime-DH0zEUpu.mjs.map +1 -0
  33. package/dist/runtime.d.mts +594 -0
  34. package/dist/runtime.d.mts.map +1 -1
  35. package/dist/runtime.mjs +1 -1
  36. package/package.json +15 -4
  37. package/dist/compat/pi-coding-agent.d.mts.map +0 -1
  38. package/dist/credentials-oauth.mjs.map +0 -1
  39. package/dist/mcp-config-DHkfjdvX.mjs +0 -707
  40. package/dist/mcp-config-DHkfjdvX.mjs.map +0 -1
  41. package/dist/oauth-bridge-CfyImWx4.mjs.map +0 -1
  42. package/dist/pi-coding-agent-CYnmeJEx.mjs.map +0 -1
  43. package/dist/pi-tui-5CYLcj-_.mjs.map +0 -1
  44. package/dist/runtime-DsPaznXJ.mjs.map +0 -1
@@ -1,4 +1,4 @@
1
1
 
2
- import { $ as isToolCallEventType, $t as formatSize, A as createEventBus, At as shouldCompact, B as getPackageDir, Bt as createEditToolDefinition, C as VERSION, Cn as createCustomMessage, Ct as estimateTokens, D as copyToClipboard, Dt as prepareBranchEntries, E as convertToPng, Et as getLastAssistantUsage, F as generateSummary, Ft as createGrepTool, G as highlightCode, Gt as createBashTool, H as getSelectListTheme, Ht as createReadTool, I as generateSummaryWithUsage, It as createGrepToolDefinition, J as isEditToolResult, Jt as truncateToVisualLines, K as initTheme, Kt as createBashToolDefinition, L as getBinDir, Lt as createWriteTool, M as defineTool, Mt as createLsToolDefinition, N as ensureTool, Nt as createFindTool, O as createAgentSession, Ot as prepareCompaction, P as generateBranchSummary, Pt as createFindToolDefinition, Q as isReadToolResult, Qt as DEFAULT_MAX_LINES, R as getLanguageFromPath, Rt as createWriteToolDefinition, S as UserMessageComponent, Sn as createCompactionSummaryMessage, St as collectEntriesForBranchSummary, T as compact, Tt as findTurnStartIndex, U as getSettingsListTheme, Ut as createReadToolDefinition, V as getReadmePath, Vt as renderDiff, W as getToolPath, Wt as bashToolSystemPromptContribution, X as isGrepToolResult, Xt as withFileMutationQueue, Y as isFindToolResult, Yt as createExtensionRuntime, Z as isLsToolResult, Zt as DEFAULT_MAX_BYTES, _ as ModelRuntime, _n as COMPACTION_SUMMARY_PREFIX, _t as wrapToolDefinitions, a as CustomMessageComponent, an as assertValidSessionId, at as rawKeyHint, b as Theme, bn as convertToLlm, bt as DEFAULT_COMPACTION_SETTINGS, c as DynamicBorder, cn as findMostRecentSession, ct as theme, d as ExtensionRunner, dn as loadEntriesFromFile, dt as loadSkills, en as truncateHead, et as isWriteToolResult, f as ExtensionSelectorComponent, fn as migrateSessionEntries, ft as loadSkillsFromDir, g as ModelRegistry, gn as BRANCH_SUMMARY_SUFFIX, gt as wrapToolDefinition, h as InMemorySettingsStorage, hn as BRANCH_SUMMARY_PREFIX, ht as wrapRegisteredTools, i as CustomEditor, in as SessionManager, it as processImage, j as createReadOnlyTools, jt as createLsTool, k as createCodingTools, kt as serializeConversation, l as ExtensionEditorComponent, ln as getDefaultSessionDir, lt as getShellConfig, m as FooterComponent, mn as sessionEntryToContextMessages, mt as wrapRegisteredTool, n as BorderedLoader, nn as truncateTail, nt as keyText, o as DefaultPackageManager, on as buildContextEntries, ot as readStoredCredential, p as FileSettingsStorage, pn as parseSessionEntries, pt as ProjectTrustStore, q as isBashToolResult, qt as createLocalBashOperations, r as CONFIG_DIR_NAME, rn as CURRENT_SESSION_VERSION, rt as parseSkillBlock, s as DefaultResourceLoader, sn as buildSessionContext, st as resizeImage, t as AssistantMessageComponent, tn as truncateLine, tt as keyHint, u as ExtensionInputComponent, un as getLatestCompactionEntry, ut as formatSkillsForPrompt, v as SettingsManager, vn as COMPACTION_SUMMARY_SUFFIX, vt as parseFrontmatter, w as __setSubagentSessionFactory, wt as findCutPoint, x as ToolExecutionComponent, xn as createBranchSummaryMessage, xt as calculateContextTokens, y as SettingsSelectorComponent, yn as bashExecutionToText, yt as stripFrontmatter, z as getMarkdownTheme, zt as createEditTool } from "../pi-coding-agent-CYnmeJEx.mjs";
2
+ import { $ as isToolCallEventType, $t as formatSize, A as createEventBus, At as shouldCompact, B as getPackageDir, Bt as createEditToolDefinition, C as VERSION, Cn as createCustomMessage, Ct as estimateTokens, D as copyToClipboard, Dt as prepareBranchEntries, E as convertToPng, Et as getLastAssistantUsage, F as generateSummary, Ft as createGrepTool, G as highlightCode, Gt as createBashTool, H as getSelectListTheme, Ht as createReadTool, I as generateSummaryWithUsage, It as createGrepToolDefinition, J as isEditToolResult, Jt as truncateToVisualLines, K as initTheme, Kt as createBashToolDefinition, L as getBinDir, Lt as createWriteTool, M as defineTool, Mt as createLsToolDefinition, N as ensureTool, Nt as createFindTool, O as createAgentSession, Ot as prepareCompaction, P as generateBranchSummary, Pt as createFindToolDefinition, Q as isReadToolResult, Qt as DEFAULT_MAX_LINES, R as getLanguageFromPath, Rt as createWriteToolDefinition, S as UserMessageComponent, Sn as createCompactionSummaryMessage, St as collectEntriesForBranchSummary, T as compact, Tt as findTurnStartIndex, U as getSettingsListTheme, Ut as createReadToolDefinition, V as getReadmePath, Vt as renderDiff, W as getToolPath, Wt as bashToolSystemPromptContribution, X as isGrepToolResult, Xt as withFileMutationQueue, Y as isFindToolResult, Yt as createExtensionRuntime, Z as isLsToolResult, Zt as DEFAULT_MAX_BYTES, _ as ModelRuntime, _n as COMPACTION_SUMMARY_PREFIX, _t as wrapToolDefinitions, a as CustomMessageComponent, an as assertValidSessionId, at as rawKeyHint, b as Theme, bn as convertToLlm, bt as DEFAULT_COMPACTION_SETTINGS, c as DynamicBorder, cn as findMostRecentSession, ct as theme, d as ExtensionRunner, dn as loadEntriesFromFile, dt as loadSkills, en as truncateHead, et as isWriteToolResult, f as ExtensionSelectorComponent, fn as migrateSessionEntries, ft as loadSkillsFromDir, g as ModelRegistry, gn as BRANCH_SUMMARY_SUFFIX, gt as wrapToolDefinition, h as InMemorySettingsStorage, hn as BRANCH_SUMMARY_PREFIX, ht as wrapRegisteredTools, i as CustomEditor, in as SessionManager, it as processImage, j as createReadOnlyTools, jt as createLsTool, k as createCodingTools, kt as serializeConversation, l as ExtensionEditorComponent, ln as getDefaultSessionDir, lt as getShellConfig, m as FooterComponent, mn as sessionEntryToContextMessages, mt as wrapRegisteredTool, n as BorderedLoader, nn as truncateTail, nt as keyText, o as DefaultPackageManager, on as buildContextEntries, ot as readStoredCredential, p as FileSettingsStorage, pn as parseSessionEntries, pt as ProjectTrustStore, q as isBashToolResult, qt as createLocalBashOperations, r as CONFIG_DIR_NAME, rn as CURRENT_SESSION_VERSION, rt as parseSkillBlock, s as DefaultResourceLoader, sn as buildSessionContext, st as resizeImage, t as AssistantMessageComponent, tn as truncateLine, tt as keyHint, u as ExtensionInputComponent, un as getLatestCompactionEntry, ut as formatSkillsForPrompt, v as SettingsManager, vn as COMPACTION_SUMMARY_SUFFIX, vt as parseFrontmatter, w as __setSubagentSessionFactory, wt as findCutPoint, x as ToolExecutionComponent, xn as createBranchSummaryMessage, xt as calculateContextTokens, y as SettingsSelectorComponent, yn as bashExecutionToText, yt as stripFrontmatter, z as getMarkdownTheme, zt as createEditTool } from "../pi-coding-agent-D9skobb6.mjs";
3
3
  import { t as getAgentDir } from "../pi-config-shim-CZ1wFzqM.mjs";
4
4
  export { AssistantMessageComponent, BRANCH_SUMMARY_PREFIX, BRANCH_SUMMARY_SUFFIX, BorderedLoader, COMPACTION_SUMMARY_PREFIX, COMPACTION_SUMMARY_SUFFIX, CONFIG_DIR_NAME, CURRENT_SESSION_VERSION, CustomEditor, CustomMessageComponent, DEFAULT_COMPACTION_SETTINGS, DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, DefaultPackageManager, DefaultResourceLoader, DynamicBorder, ExtensionEditorComponent, ExtensionInputComponent, ExtensionRunner, ExtensionSelectorComponent, FileSettingsStorage, FooterComponent, InMemorySettingsStorage, ModelRegistry, ModelRuntime, ProjectTrustStore, SessionManager, SettingsManager, SettingsSelectorComponent, Theme, ToolExecutionComponent, UserMessageComponent, VERSION, __setSubagentSessionFactory, assertValidSessionId, bashExecutionToText, bashToolSystemPromptContribution, buildContextEntries, buildSessionContext, calculateContextTokens, collectEntriesForBranchSummary, compact, convertToLlm, convertToPng, copyToClipboard, createAgentSession, createBashTool, createBashToolDefinition, createBranchSummaryMessage, createCodingTools, createCompactionSummaryMessage, createCustomMessage, createEditTool, createEditToolDefinition, createEventBus, createExtensionRuntime, createFindTool, createFindToolDefinition, createGrepTool, createGrepToolDefinition, createLocalBashOperations, createLsTool, createLsToolDefinition, createReadOnlyTools, createReadTool, createReadToolDefinition, createWriteTool, createWriteToolDefinition, defineTool, ensureTool, estimateTokens, findCutPoint, findMostRecentSession, findTurnStartIndex, formatSize, formatSkillsForPrompt, generateBranchSummary, generateSummary, generateSummaryWithUsage, getAgentDir, getBinDir, getDefaultSessionDir, getLanguageFromPath, getLastAssistantUsage, getLatestCompactionEntry, getMarkdownTheme, getPackageDir, getReadmePath, getSelectListTheme, getSettingsListTheme, getShellConfig, getToolPath, highlightCode, initTheme, isBashToolResult, isEditToolResult, isFindToolResult, isGrepToolResult, isLsToolResult, isReadToolResult, isToolCallEventType, isWriteToolResult, keyHint, keyText, loadEntriesFromFile, loadSkills, loadSkillsFromDir, migrateSessionEntries, parseFrontmatter, parseSessionEntries, parseSkillBlock, prepareBranchEntries, prepareCompaction, processImage, rawKeyHint, readStoredCredential, renderDiff, resizeImage, serializeConversation, sessionEntryToContextMessages, shouldCompact, stripFrontmatter, theme, truncateHead, truncateLine, truncateTail, truncateToVisualLines, withFileMutationQueue, wrapRegisteredTool, wrapRegisteredTools, wrapToolDefinition, wrapToolDefinitions };
@@ -1,3 +1,3 @@
1
1
 
2
- import { $ as matchesKey, A as getCellDimensions, B as setCellDimensions, C as calculateImageRows, D as encodeITerm2, E as detectCapabilities, F as getWebpDimensions, G as setKeybindings, H as KeybindingsManager, I as hyperlink, J as Key, K as parseOsc11BackgroundColor, L as imageFallback, M as getImageDimensions, N as getJpegDimensions, O as encodeKitty, P as getPngDimensions, Q as isKittyProtocolActive, R as resetCapabilitiesCache, S as allocateImageId, T as deleteKittyImage, U as TUI_KEYBINDINGS, V as renderLatex, W as getKeybindings, X as isKeyRelease, Y as decodeKittyPrintable, Z as isKeyRepeat, _ as TruncatedText, _t as stripTerminalSequences, a as Editor, at as applyBackgroundToLine, b as isViewportTUI, bt as wrapTextWithAnsi, c as Input, ct as getGraphemeCellRange, d as Marked, dt as getWordSegmenter, et as parseKey, f as ScrollView, ft as isPunctuationChar, g as Text, gt as sliceWithWidth, h as Spacer, ht as sliceByColumn, i as Container, it as PUNCTUATION_REGEX, j as getGifDimensions, k as getCapabilities, l as Loader, lt as getGraphemeSegmenter, m as SettingsList, mt as normalizeTerminalOutput, n as CURSOR_MARKER, nt as fuzzyFilter, o as HStack, ot as cjkBreakRegex, p as SelectList, pt as isWhitespaceChar, q as parseTerminalColorSchemeReport, r as CancellableLoader, rt as fuzzyMatch, s as Image, st as extractAnsiCode, t as Box, tt as setKittyProtocolActive, u as Markdown, ut as getOsc8LinkAtColumn, v as VStack, vt as truncateToWidth, w as deleteAllKittyImages, x as CombinedAutocompleteProvider, y as isFocusable, yt as visibleWidth, z as setCapabilities } from "../pi-tui-5CYLcj-_.mjs";
2
+ import { $ as matchesKey, A as getCellDimensions, B as setCellDimensions, C as calculateImageRows, D as encodeITerm2, E as detectCapabilities, F as getWebpDimensions, G as setKeybindings, H as KeybindingsManager, I as hyperlink, J as Key, K as parseOsc11BackgroundColor, L as imageFallback, M as getImageDimensions, N as getJpegDimensions, O as encodeKitty, P as getPngDimensions, Q as isKittyProtocolActive, R as resetCapabilitiesCache, S as allocateImageId, T as deleteKittyImage, U as TUI_KEYBINDINGS, V as renderLatex, W as getKeybindings, X as isKeyRelease, Y as decodeKittyPrintable, Z as isKeyRepeat, _ as TruncatedText, _t as stripTerminalSequences, a as Editor, at as applyBackgroundToLine, b as isViewportTUI, bt as wrapTextWithAnsi, c as Input, ct as getGraphemeCellRange, d as Marked, dt as getWordSegmenter, et as parseKey, f as ScrollView, ft as isPunctuationChar, g as Text, gt as sliceWithWidth, h as Spacer, ht as sliceByColumn, i as Container, it as PUNCTUATION_REGEX, j as getGifDimensions, k as getCapabilities, l as Loader, lt as getGraphemeSegmenter, m as SettingsList, mt as normalizeTerminalOutput, n as CURSOR_MARKER, nt as fuzzyFilter, o as HStack, ot as cjkBreakRegex, p as SelectList, pt as isWhitespaceChar, q as parseTerminalColorSchemeReport, r as CancellableLoader, rt as fuzzyMatch, s as Image, st as extractAnsiCode, t as Box, tt as setKittyProtocolActive, u as Markdown, ut as getOsc8LinkAtColumn, v as VStack, vt as truncateToWidth, w as deleteAllKittyImages, x as CombinedAutocompleteProvider, y as isFocusable, yt as visibleWidth, z as setCapabilities } from "../pi-tui-DruSeKmd.mjs";
3
3
  export { Box, CURSOR_MARKER, CancellableLoader, CombinedAutocompleteProvider, Container, Editor, HStack, Image, Input, Key, KeybindingsManager, Loader, Markdown, Marked, PUNCTUATION_REGEX, ScrollView, SelectList, SettingsList, Spacer, TUI_KEYBINDINGS, Text, TruncatedText, VStack, allocateImageId, applyBackgroundToLine, calculateImageRows, cjkBreakRegex, decodeKittyPrintable, deleteAllKittyImages, deleteKittyImage, detectCapabilities, encodeITerm2, encodeKitty, extractAnsiCode, fuzzyFilter, fuzzyMatch, getCapabilities, getCellDimensions, getGifDimensions, getGraphemeCellRange, getGraphemeSegmenter, getImageDimensions, getJpegDimensions, getKeybindings, getOsc8LinkAtColumn, getPngDimensions, getWebpDimensions, getWordSegmenter, hyperlink, imageFallback, isFocusable, isKeyRelease, isKeyRepeat, isKittyProtocolActive, isPunctuationChar, isViewportTUI, isWhitespaceChar, matchesKey, normalizeTerminalOutput, parseKey, parseOsc11BackgroundColor, parseTerminalColorSchemeReport, renderLatex, resetCapabilitiesCache, setCapabilities, setCellDimensions, setKeybindings, setKittyProtocolActive, sliceByColumn, sliceWithWidth, stripTerminalSequences, truncateToWidth, visibleWidth, wrapTextWithAnsi };
@@ -1,7 +1,11 @@
1
1
 
2
+ import { c as builtinProviders } from "./pi-ai-BA3V_J0V.mjs";
3
+ import "./pi-coding-agent-D9skobb6.mjs";
4
+ import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
2
5
  import { mkdir, rename, writeFile } from "node:fs/promises";
3
6
  import { existsSync, readFileSync } from "node:fs";
4
- import { dirname } from "node:path";
7
+ import { dirname, join } from "node:path";
8
+ import { CredentialProvider } from "@deepseek-ai/dsh-credentials";
5
9
  //#region src/compat/vendor/pi-ai-abort.ts
6
10
  function abortReason(signal) {
7
11
  if (signal.reason !== void 0) return signal.reason;
@@ -304,21 +308,85 @@ var FileCredentialStore = class extends InMemoryCredentialStore {
304
308
  return super.delete(providerId, options).then(() => this.#persist());
305
309
  }
306
310
  };
307
- function oauthInteraction(ui, signal) {
311
+ /**
312
+ * One signal that fires when EITHER input does.
313
+ *
314
+ * A question on screen has two reasons to close: the flow's own race (the
315
+ * browser callback beating the paste box) and the whole login being called off.
316
+ * @param ambient - the login's signal, live for the whole flow.
317
+ * @param perCall - the flow's signal for this one question, when it sent one.
318
+ * @returns the joined signal, or whichever one exists.
319
+ */
320
+ function joinSignals(ambient, perCall) {
321
+ if (!(perCall instanceof AbortSignal)) return ambient;
322
+ if (ambient === void 0) return perCall;
323
+ const joined = new AbortController();
324
+ const stop = () => joined.abort();
325
+ if (ambient.aborted || perCall.aborted) joined.abort();
326
+ else {
327
+ ambient.addEventListener("abort", stop, { once: true });
328
+ perCall.addEventListener("abort", stop, { once: true });
329
+ }
330
+ return joined.signal;
331
+ }
332
+ /**
333
+ * Hand a URL to the host's browser opener, when the host gave one.
334
+ *
335
+ * Opening a window is the HOST's action, not the translation layer's, so the
336
+ * opener is injected rather than reached for. Wiring it in here meant every
337
+ * caller opened a real browser tab — including the test suite, which spawned a
338
+ * tab per assertion on the developer's own machine.
339
+ * @param open - the host's opener, absent when the host does not open windows.
340
+ * @param url - the address the flow announced, when it announced one.
341
+ */
342
+ function openIfPossible(open, url) {
343
+ if (open === void 0 || typeof url !== "string" || !/^https?:\/\//u.test(url)) return;
344
+ try {
345
+ open(url);
346
+ } catch {}
347
+ }
348
+ function oauthInteraction(ui, signal, shorten, open) {
349
+ const effectiveSignal = signal ?? new AbortController().signal;
350
+ let pending;
351
+ /**
352
+ * Hand the announced address to the prompt as its DETAIL, not its title.
353
+ *
354
+ * An authorization URL is 400 characters of query string. Prepending it to
355
+ * the question turned the dialog into a wall of wrapped text with the actual
356
+ * question buried underneath; `detail` is the slot DSH renders supporting
357
+ * text in.
358
+ * @param placeholder - the flow's own placeholder, kept when it supplied one.
359
+ */
360
+ const takePending = (placeholder) => {
361
+ const carried = pending;
362
+ pending = void 0;
363
+ if (carried !== void 0) return carried;
364
+ return placeholder === void 0 ? void 0 : String(placeholder);
365
+ };
308
366
  return {
309
- signal: signal ?? new AbortController().signal,
367
+ signal: effectiveSignal,
310
368
  async prompt(prompt) {
311
369
  if (prompt.type === "select") {
312
370
  const options = prompt.options ?? [];
313
- const picked = await ui.select(prompt.message, options.map((option) => option.label));
371
+ pending = void 0;
372
+ const picked = await ui.select(prompt.message, options.map((option) => option.label), effectiveSignal);
314
373
  return options.find((option) => option.label === picked)?.id ?? picked;
315
374
  }
316
- return ui.input(prompt.message, prompt.placeholder);
375
+ return ui.input(prompt.message, takePending(prompt.placeholder), joinSignals(effectiveSignal, prompt.signal));
317
376
  },
318
377
  notify(event) {
319
- if (event.type === "auth_url") ui.notify(`Open this URL to authorize: ${event.url}${event.instructions !== void 0 ? `\n${event.instructions}` : ""}`);
320
- else if (event.type === "device_code") ui.notify(`Visit ${event.verificationUri} and enter code ${event.userCode}`);
321
- else if (event.message !== void 0) ui.notify(event.message);
378
+ if (event.type === "auth_url") {
379
+ const shown = (event.url === void 0 ? void 0 : shorten?.(event.url)) ?? event.url;
380
+ const tail = event.instructions === void 0 ? "" : `\n\n${event.instructions}`;
381
+ pending = `[Open the login page](${shown})${tail}`;
382
+ ui.notify(`Open this URL to authorize: ${shown}${tail}`);
383
+ openIfPossible(open, event.url);
384
+ } else if (event.type === "device_code") {
385
+ const window = typeof event.expiresInSeconds === "number" && event.expiresInSeconds > 0 ? ` (valid for ${Math.round(event.expiresInSeconds / 60)} min)` : "";
386
+ pending = `[Open the device login page](${event.verificationUri}) and enter code \`${event.userCode}\`${window}`;
387
+ ui.notify(`Visit ${event.verificationUri} and enter code ${event.userCode}${window}`);
388
+ openIfPossible(open, event.verificationUri);
389
+ } else if (event.message !== void 0) ui.notify(event.message);
322
390
  }
323
391
  };
324
392
  }
@@ -336,9 +404,19 @@ function providerSupportsOAuth(providerConfig) {
336
404
  async function loginPiProvider(options) {
337
405
  const oauthConfig = oauthConfigOf(options.providerConfig);
338
406
  if (oauthConfig === void 0) throw new Error(`${options.providerName ?? options.providerId} does not support oauth login`);
339
- const credential = await oauthAdapterOf(oauthConfig).login(oauthInteraction(options.ui, options.signal));
340
- await options.store.modify(options.providerId, async () => credential);
341
- return credential;
407
+ const adapter = oauthAdapterOf(oauthConfig);
408
+ const done = new AbortController();
409
+ if (options.signal !== void 0) {
410
+ if (options.signal.aborted) done.abort();
411
+ else options.signal.addEventListener("abort", () => done.abort(), { once: true });
412
+ }
413
+ try {
414
+ const credential = await adapter.login(oauthInteraction(options.ui, done.signal, options.shorten, options.open));
415
+ await options.store.modify(options.providerId, async () => credential);
416
+ return credential;
417
+ } finally {
418
+ done.abort();
419
+ }
342
420
  }
343
421
  async function resolveOAuthApiKey(options) {
344
422
  const oauthConfig = oauthConfigOf(options.providerConfig);
@@ -370,6 +448,87 @@ async function resolvePiProviderAuth(options) {
370
448
  }, options.signal !== void 0 ? { signal: options.signal } : void 0);
371
449
  }
372
450
  //#endregion
373
- export { resolvePiProviderAuth as a, resolveOAuthApiKey as i, loginPiProvider as n, storedOAuthCredential as o, providerSupportsOAuth as r, FileCredentialStore as t };
451
+ //#region src/credentials-oauth.ts
452
+ const OAUTH_REF_PREFIX = "PI2DSH_OAUTH_";
453
+ function oauthCredentialRef(providerId) {
454
+ return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll("-", "_")}`;
455
+ }
456
+ function providerIdOfRef(ref) {
457
+ if (!ref.startsWith(OAUTH_REF_PREFIX)) return void 0;
458
+ return ref.slice(13).toLowerCase().replaceAll("_", "-");
459
+ }
460
+ var PiOAuthCredentialProvider = class extends CredentialProvider {
461
+ oauthStore;
462
+ extraProviders;
463
+ constructor(ctx, options = {}) {
464
+ super(ctx);
465
+ this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), "auth.json"));
466
+ this.extraProviders = options.providers ?? /* @__PURE__ */ new Map();
467
+ }
468
+ oauthConfigFor(providerId) {
469
+ const registered = this.extraProviders.get(providerId);
470
+ if (providerSupportsOAuth(registered)) return registered;
471
+ const builtin = builtinProviders().find((provider) => provider.id === providerId);
472
+ if (builtin === void 0) return void 0;
473
+ return {
474
+ name: builtin.name,
475
+ baseUrl: builtin.baseUrl,
476
+ oauth: builtin.auth.oauth
477
+ };
478
+ }
479
+ async resolve(ref) {
480
+ const providerId = providerIdOfRef(String(ref));
481
+ if (providerId === void 0) {
482
+ const value = process.env[String(ref)];
483
+ return value !== void 0 && value.length > 0 ? {
484
+ value,
485
+ source: "env"
486
+ } : void 0;
487
+ }
488
+ const config = this.oauthConfigFor(providerId);
489
+ if (config === void 0) return void 0;
490
+ const value = await resolveOAuthApiKey({
491
+ providerId,
492
+ providerName: typeof config.name === "string" ? config.name : providerId,
493
+ providerConfig: config,
494
+ store: this.oauthStore
495
+ });
496
+ return value !== void 0 && value.length > 0 ? {
497
+ value,
498
+ source: "pi-oauth"
499
+ } : void 0;
500
+ }
501
+ async describe(ref) {
502
+ const providerId = providerIdOfRef(String(ref));
503
+ if (providerId === void 0) {
504
+ const value = process.env[String(ref)];
505
+ return {
506
+ configured: value !== void 0 && value.length > 0,
507
+ ...value ? { source: "env" } : {},
508
+ writable: false
509
+ };
510
+ }
511
+ const stored = await storedOAuthCredential(this.oauthStore, providerId);
512
+ return {
513
+ configured: stored !== void 0,
514
+ ...stored !== void 0 ? { source: "pi-oauth" } : {},
515
+ writable: false
516
+ };
517
+ }
518
+ async set(ref, _value) {
519
+ throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`);
520
+ }
521
+ async unset(ref) {
522
+ const providerId = providerIdOfRef(String(ref));
523
+ if (providerId === void 0) throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`);
524
+ await this.oauthStore.delete(providerId, void 0);
525
+ }
526
+ };
527
+ const name = "pi2dsh-credentials-oauth";
528
+ function apply(ctx, config = {}) {
529
+ new PiOAuthCredentialProvider(ctx, config);
530
+ }
531
+ //#endregion
532
+ export { FileCredentialStore as a, providerSupportsOAuth as c, oauthCredentialRef as i, resolvePiProviderAuth as l, apply as n, joinSignals as o, name as r, loginPiProvider as s, PiOAuthCredentialProvider as t, storedOAuthCredential as u };
374
533
 
375
- //# sourceMappingURL=oauth-bridge-CfyImWx4.mjs.map
534
+ //# sourceMappingURL=credentials-oauth-BakY-0PA.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials-oauth-BakY-0PA.mjs","names":["#path","#persist"],"sources":["../src/compat/vendor/pi-ai-abort.ts","../src/compat/vendor/pi-ai-credential-store.ts","../src/compat/vendor/pi-ai-diagnostics.ts","../src/compat/vendor/pi-ai-auth-resolve.ts","../src/compat/vendor/pi-oauth-adapt.ts","../src/oauth-bridge.ts","../src/credentials-oauth.ts"],"sourcesContent":["// @ts-nocheck — vendored Pi source (pi-ai utils/abort.js @0.84.1, MIT, see ./PI-LICENSE); unchanged.\nfunction abortReason(signal) {\n if (signal.reason !== undefined)\n return signal.reason;\n const error = new Error(\"The operation was aborted\");\n error.name = \"AbortError\";\n return error;\n}\n/** Create an operation-local signal for public APIs whose signal is optional. */\nexport function operationSignal(signal) {\n return signal ?? new AbortController().signal;\n}\n/**\n * Stop waiting for an operation when its signal aborts while continuing to\n * observe the abandoned promise so a later rejection is always handled.\n */\nexport function raceWithAbortSignal(operation, signal) {\n if (signal.aborted) {\n void operation.catch(() => { });\n return Promise.reject(abortReason(signal));\n }\n return new Promise((resolve, reject) => {\n let settled = false;\n const cleanup = () => signal.removeEventListener(\"abort\", onAbort);\n const onAbort = () => {\n if (settled)\n return;\n settled = true;\n cleanup();\n reject(abortReason(signal));\n };\n signal.addEventListener(\"abort\", onAbort, { once: true });\n void operation.then((value) => {\n if (settled)\n return;\n settled = true;\n cleanup();\n resolve(value);\n }, (error) => {\n if (settled)\n return;\n settled = true;\n cleanup();\n reject(error);\n });\n if (signal.aborted)\n onAbort();\n });\n}\n//# sourceMappingURL=abort.js.map","// @ts-nocheck — vendored Pi source (pi-ai auth/credential-store.js @0.84.1, MIT, see ./PI-LICENSE); import remapped, logic unchanged.\nimport { operationSignal, raceWithAbortSignal } from './pi-ai-abort.js';\n/**\n * Default in-memory credential store. Apps inject persistent stores.\n * Keyed by `Provider.id`, one credential per provider; see `CredentialStore`.\n * Writes are serialized per provider through a promise chain.\n */\nexport class InMemoryCredentialStore {\n credentials = new Map();\n chains = new Map();\n /** Serialize tasks per provider id without releasing the chain before active work settles. */\n enqueue(providerId, task, options) {\n const signal = operationSignal(options?.signal);\n const previous = this.chains.get(providerId) ?? Promise.resolve();\n const queued = (async () => {\n await previous.catch(() => { });\n signal.throwIfAborted();\n return task();\n })();\n const tail = queued.catch(() => { });\n this.chains.set(providerId, tail);\n void tail.then(() => {\n if (this.chains.get(providerId) === tail)\n this.chains.delete(providerId);\n });\n return raceWithAbortSignal(queued, signal);\n }\n async read(providerId, options) {\n options?.signal?.throwIfAborted();\n return this.credentials.get(providerId);\n }\n async list(options) {\n options?.signal?.throwIfAborted();\n return [...this.credentials].map(([providerId, credential]) => ({ providerId, type: credential.type }));\n }\n modify(providerId, fn, options) {\n return this.enqueue(providerId, async () => {\n const current = this.credentials.get(providerId);\n const next = await fn(current);\n options?.signal?.throwIfAborted();\n if (next !== undefined)\n this.credentials.set(providerId, next);\n return next ?? current;\n }, options);\n }\n delete(providerId, options) {\n return this.enqueue(providerId, async () => {\n this.credentials.delete(providerId);\n }, options);\n }\n}\n//# sourceMappingURL=credential-store.js.map","// @ts-nocheck — vendored Pi source (pi-ai utils/diagnostics.js @0.84.1, MIT, see ./PI-LICENSE); unchanged.\nexport function formatThrownValue(value) {\n if (value instanceof Error)\n return value.message || value.name;\n if (typeof value === \"string\")\n return value;\n return String(value);\n}\nexport function extractDiagnosticError(error) {\n if (!(error instanceof Error))\n return { name: \"ThrownValue\", message: formatThrownValue(error) };\n const code = error.code;\n return {\n name: error.name || undefined,\n message: error.message || error.name,\n stack: error.stack,\n code: typeof code === \"string\" || typeof code === \"number\" ? code : undefined,\n };\n}\nexport function createAssistantMessageDiagnostic(type, error, details) {\n return { type, timestamp: Date.now(), error: extractDiagnosticError(error), details };\n}\nexport function appendAssistantMessageDiagnostic(message, diagnostic) {\n message.diagnostics = [...(message.diagnostics ?? []), diagnostic];\n}\n//# sourceMappingURL=diagnostics.js.map","// @ts-nocheck — vendored Pi source (pi-ai auth/resolve.js @0.84.1, MIT, see ./PI-LICENSE); imports remapped, logic unchanged\n// (double-checked-lock OAuth refresh with persistence, no silent env fallback).\nimport { operationSignal, raceWithAbortSignal } from './pi-ai-abort.js';\nimport { formatThrownValue } from './pi-ai-diagnostics.js';\nexport class ModelsError extends Error {\n code;\n constructor(code, message, options) {\n super(withCauseDetail(message, options?.cause), options);\n this.name = \"ModelsError\";\n this.code = code;\n }\n}\n/** Callers surface `error.message` only, so keep the underlying reason in it. */\nfunction withCauseDetail(message, cause) {\n if (cause === undefined || cause === null)\n return message;\n const detail = formatThrownValue(cause).trim();\n if (!detail || message.includes(detail))\n return message;\n return `${message}: ${detail}`;\n}\n/**\n * Auth resolution shared by the `Models` and `ImagesModels` collections.\n * A stored credential owns the provider: ambient/env is consulted only when\n * nothing is stored. No silent env fallback after a failed refresh or for a\n * credential type without a matching handler.\n */\nexport function resolveProviderAuth(provider, credentials, authContext, overrides) {\n const signal = operationSignal(overrides?.signal);\n return raceWithAbortSignal(resolveProviderAuthWithSignal(provider, credentials, authContext, overrides, signal), signal);\n}\nasync function resolveProviderAuthWithSignal(provider, credentials, authContext, overrides, signal) {\n signal.throwIfAborted();\n const requestAuthContext = overrides?.env ? overlayEnvAuthContext(authContext, overrides.env) : authContext;\n if (overrides?.apiKey !== undefined && provider.auth.apiKey) {\n return resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, {\n type: \"api_key\",\n key: overrides.apiKey,\n env: overrides.env,\n }, signal);\n }\n const stored = await readCredential(credentials, provider.id, signal);\n if (stored) {\n if (stored.type === \"oauth\" && provider.auth.oauth) {\n return resolveStoredOAuth(credentials, provider.id, provider.auth.oauth, stored, signal, overrides?.minOAuthValidityMs);\n }\n if (stored.type === \"api_key\" && provider.auth.apiKey) {\n const credential = overrides?.env ? { ...stored, env: { ...stored.env, ...overrides.env } } : stored;\n return resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, credential, signal);\n }\n return undefined;\n }\n // Ambient (env vars, AWS profiles, ADC files).\n return provider.auth.apiKey\n ? resolveApiKey(requestAuthContext, provider.auth.apiKey, provider.id, undefined, signal)\n : undefined;\n}\nfunction overlayEnvAuthContext(base, env) {\n return {\n env: async (name) => env[name] || (await base.env(name)),\n fileExists: (path) => base.fileExists(path),\n };\n}\nconst DEFAULT_OAUTH_MINIMUM_VALIDITY_MS = 5 * 60 * 1000;\nconst DEFAULT_OAUTH_REFRESH_TIMEOUT_MS = 15_000;\n/**\n * OAuth resolution with double-checked locking: tokens with less than five\n * minutes remaining lock, re-check expiry under the lock, refresh once\n * globally, and persist the rotated credential before release.\n */\nasync function resolveStoredOAuth(credentials, providerId, oauth, stored, signal, minOAuthValidityMs) {\n const minimumValidityMs = Math.max(DEFAULT_OAUTH_MINIMUM_VALIDITY_MS, minOAuthValidityMs ?? 0);\n const expiresSoon = (credential) => Date.now() + minimumValidityMs >= credential.expires;\n let credential = stored;\n if (expiresSoon(credential)) {\n // Optimistic check said expired; the authoritative check runs under the lock.\n let post;\n try {\n post = await credentials.modify(providerId, async (current) => {\n if (current?.type !== \"oauth\")\n return undefined; // logged out meanwhile\n if (!expiresSoon(current))\n return undefined; // another process/request refreshed\n try {\n const refreshSignal = AbortSignal.any([\n signal,\n AbortSignal.timeout(DEFAULT_OAUTH_REFRESH_TIMEOUT_MS),\n ]);\n return await oauth.refresh(current, refreshSignal);\n }\n catch (error) {\n throw new ModelsError(\"oauth\", `OAuth refresh failed for ${providerId}`, { cause: error });\n }\n }, { signal });\n }\n catch (error) {\n if (error instanceof ModelsError)\n throw error;\n throw new ModelsError(\"auth\", `Credential store modify failed for ${providerId}`, { cause: error });\n }\n if (post?.type !== \"oauth\")\n return undefined; // logged out meanwhile\n credential = post;\n // The normal five-minute window triggers a refresh but does not impose a\n // provider contract. Explicit callers (such as bearer-token export) do\n // require the requested minimum after the refresh.\n if (minOAuthValidityMs !== undefined && expiresSoon(credential)) {\n throw new ModelsError(\"oauth\", `OAuth refresh returned a token that expires too soon for ${providerId}`);\n }\n }\n try {\n return { auth: await oauth.toAuth(credential), source: \"OAuth\" };\n }\n catch (error) {\n throw new ModelsError(\"oauth\", `OAuth auth derivation failed for ${providerId}`, { cause: error });\n }\n}\nasync function resolveApiKey(authContext, apiKey, providerId, credential, signal) {\n try {\n return await apiKey.resolve({ ctx: authContext, credential, signal });\n }\n catch (error) {\n throw new ModelsError(\"auth\", `API key auth failed for provider ${providerId}`, { cause: error });\n }\n}\nasync function readCredential(credentials, providerId, signal) {\n try {\n return await credentials.read(providerId, { signal });\n }\n catch (error) {\n throw new ModelsError(\"auth\", `Credential store read failed for ${providerId}`, { cause: error });\n }\n}\n//# sourceMappingURL=resolve.js.map","// @ts-nocheck — vendored Pi source excerpt (pi-coding-agent core/provider-composer.js adaptOAuth @0.84.1, MIT, see ./PI-LICENSE); logic unchanged.\n// The official two-generation adapter: extension oauth callbacks (onAuth/onDeviceCode/onPrompt/onSelect)\n// over the pi-ai interaction surface (prompt/notify/signal), plus refresh and toAuth->getApiKey.\nexport function adaptOAuth(config) {\n return {\n name: config.name,\n isSubscription: config.isSubscription,\n login: async (callbacks) => {\n const credential = await config.login({\n onAuth: (info) => callbacks.notify({ type: \"auth_url\", ...info }),\n onDeviceCode: (info) => callbacks.notify({ type: \"device_code\", ...info }),\n onPrompt: (prompt) => callbacks.prompt({ type: \"text\", ...prompt }),\n onProgress: (message) => callbacks.notify({ type: \"progress\", message }),\n onManualCodeInput: () => callbacks.prompt({ type: \"manual_code\", message: \"Paste the authorization code\" }),\n onSelect: (prompt) => callbacks.prompt({ type: \"select\", ...prompt }),\n signal: callbacks.signal,\n });\n return { ...credential, type: \"oauth\" };\n },\n refresh: async (credential, signal) => ({ ...(await config.refreshToken(credential, signal)), type: \"oauth\" }),\n toAuth: async (credential) => ({ apiKey: config.getApiKey(credential) }),\n };\n}\n","import { existsSync, readFileSync } from 'node:fs'\nimport { mkdir, rename, writeFile } from 'node:fs/promises'\nimport { dirname } from 'node:path'\nimport { InMemoryCredentialStore } from './compat/vendor/pi-ai-credential-store.js'\nimport { resolveProviderAuth } from './compat/vendor/pi-ai-auth-resolve.js'\nimport { adaptOAuth } from './compat/vendor/pi-oauth-adapt.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// The interactive OAuth host seam, four layers with one owner each:\n// L1 protocol — the package's own oauth.login/refreshToken/getApiKey code\n// runs verbatim (device-code, auth-code, rotation). The\n// bridge implements zero protocol.\n// L2 interaction— Pi's official adaptOAuth (vendored) turns the package's\n// callbacks into prompt/notify events; this file routes\n// those onto the Pi ui surface, which pi2dsh already maps\n// to DSH userQuestions with real waits.\n// L3 credentials— Pi's auth.json contract: one file keyed by provider id,\n// 0600, atomic replace, per-provider serialized writes\n// (the vendored InMemoryCredentialStore chain), refresh via\n// Pi's vendored double-checked-lock resolver.\n// L4 routing — resolveOAuthApiKey yields the request key for dsh-llm's\n// one-shot credential seam; tokens never enter DSH settings.\n\nexport class FileCredentialStore extends InMemoryCredentialStore {\n readonly #path: string\n\n constructor(path: string) {\n super()\n this.#path = path\n try {\n const data = JSON.parse(readFileSync(path, 'utf8')) as Record<string, UnknownRecord>\n for (const [providerId, credential] of Object.entries(data)) {\n ;(this as unknown as { credentials: Map<string, unknown> }).credentials.set(providerId, credential)\n }\n } catch {\n // Missing or unreadable auth.json starts empty, exactly like Pi.\n }\n }\n\n get path(): string {\n return this.#path\n }\n\n async #persist(): Promise<void> {\n const credentials = (this as unknown as { credentials: Map<string, unknown> }).credentials\n const data = Object.fromEntries(credentials)\n await mkdir(dirname(this.#path), { recursive: true })\n // Atomic replace so a crash mid-write never truncates the store; 0600 so\n // tokens stay owner-readable only. Same contract Pi's file store keeps.\n const temp = `${this.#path}.tmp-${process.pid}-${Math.floor(Math.random() * 1e9).toString(36)}`\n await writeFile(temp, `${JSON.stringify(data, null, 2)}\\n`, { mode: 0o600 })\n await rename(temp, this.#path)\n }\n\n override modify(providerId: string, fn: (current: unknown) => unknown, options?: UnknownRecord): Promise<unknown> {\n // The vendored per-provider chain already serializes writers; persisting\n // inside the chained task keeps disk order identical to memory order.\n return super.modify(providerId, async (current: unknown) => {\n const next = await fn(current)\n if (next !== undefined) {\n const credentials = (this as unknown as { credentials: Map<string, unknown> }).credentials\n credentials.set(providerId, next)\n await this.#persist()\n }\n return next\n }, options)\n }\n\n override delete(providerId: string, options?: UnknownRecord): Promise<void> {\n return super.delete(providerId, options).then(() => this.#persist()) as Promise<void>\n }\n}\n\n/**\n * One signal that fires when EITHER input does.\n *\n * A question on screen has two reasons to close: the flow's own race (the\n * browser callback beating the paste box) and the whole login being called off.\n * @param ambient - the login's signal, live for the whole flow.\n * @param perCall - the flow's signal for this one question, when it sent one.\n * @returns the joined signal, or whichever one exists.\n */\nexport function joinSignals(ambient: AbortSignal | undefined, perCall: unknown): AbortSignal | undefined {\n if (!(perCall instanceof AbortSignal)) return ambient\n if (ambient === undefined) return perCall\n const joined = new AbortController()\n const stop = () => joined.abort()\n if (ambient.aborted || perCall.aborted) joined.abort()\n else {\n ambient.addEventListener('abort', stop, { once: true })\n perCall.addEventListener('abort', stop, { once: true })\n }\n return joined.signal\n}\n\nexport interface OAuthUiSurface {\n /**\n * @param signal cancels THIS question when another path in the flow answers\n * first — the browser callback beating the paste box. Optional because\n * Pi's own ui.input takes two arguments; the bridge's accepts a third.\n */\n input(title: unknown, placeholder?: unknown, signal?: AbortSignal): Promise<string | undefined>\n select(title: unknown, options: unknown[], signal?: AbortSignal): Promise<string | undefined>\n notify(message: unknown): void\n}\n\ninterface OAuthPromptEvent {\n type: 'text' | 'manual_code' | 'select'\n message: string\n placeholder?: string\n options?: Array<{ id: string, label: string }>\n /** Cancels THIS question when another path in the flow answers first. */\n signal?: AbortSignal\n}\n\ninterface OAuthNotifyEvent {\n type: 'auth_url' | 'device_code' | 'progress'\n url?: string\n instructions?: string\n userCode?: string\n verificationUri?: string\n message?: string\n /** How long the device code stays valid. Every device-code flow sends it. */\n expiresInSeconds?: number\n /** How often the flow polls; sent alongside the expiry. */\n intervalSeconds?: number\n}\n\n// The pi-ai interaction surface ({prompt, notify, signal}) over the Pi ui\n// surface pi2dsh already maps to DSH userQuestions.\n/**\n * Hand a URL to the host's browser opener, when the host gave one.\n *\n * Opening a window is the HOST's action, not the translation layer's, so the\n * opener is injected rather than reached for. Wiring it in here meant every\n * caller opened a real browser tab — including the test suite, which spawned a\n * tab per assertion on the developer's own machine.\n * @param open - the host's opener, absent when the host does not open windows.\n * @param url - the address the flow announced, when it announced one.\n */\nfunction openIfPossible(open: ((url: string) => void) | undefined, url: string | undefined): void {\n if (open === undefined || typeof url !== 'string' || !/^https?:\\/\\//u.test(url)) return\n try {\n open(url)\n } catch {\n // Best effort by design: the URL is in the prompt either way, and a host\n // with no desktop session (a container, a remote box) must still be able to\n // finish the login by pasting the code.\n }\n}\n\nexport function oauthInteraction(\n ui: OAuthUiSurface,\n signal?: AbortSignal,\n shorten?: (url: string) => string | undefined,\n open?: (url: string) => void,\n): {\n prompt(prompt: OAuthPromptEvent): Promise<string | undefined>\n notify(event: OAuthNotifyEvent): void\n signal: AbortSignal\n} {\n // Pi hosts always hand flows a live signal; flows attach abort listeners\n // unconditionally, so an absent caller signal becomes a never-aborting one.\n const effectiveSignal = signal ?? new AbortController().signal\n // Where the user has to GO to authorize, held until the next prompt.\n //\n // The flow announces the authorization URL through notify() and then blocks\n // on prompt() for the code. A notice is a command notice on DSH: it reaches\n // the user when the command ENDS — and this command cannot end until the user\n // acts on the notice. So the address was delivered after it was needed, and\n // the dialog on screen showed only the redirect URI (localhost/auth/callback),\n // which does nothing when clicked. Carrying it into the prompt puts it where\n // the user is already looking.\n let pending: string | undefined\n /**\n * Hand the announced address to the prompt as its DETAIL, not its title.\n *\n * An authorization URL is 400 characters of query string. Prepending it to\n * the question turned the dialog into a wall of wrapped text with the actual\n * question buried underneath; `detail` is the slot DSH renders supporting\n * text in.\n * @param placeholder - the flow's own placeholder, kept when it supplied one.\n */\n const takePending = (placeholder: unknown): string | undefined => {\n const carried = pending\n pending = undefined\n // A carried address takes the slot alone. Pi's placeholder is greyed hint\n // text INSIDE its input box; DSH's dialog has no such slot, so it lands in\n // the same body text — and codex's placeholder is the redirect URI\n // (localhost:1455/auth/callback), which GFM autolinks into a second,\n // clickable, useless address sitting right under the real one. That is the\n // trap the user already fell into. Where nothing was announced it is still\n // the only hint there is, so it stays.\n if (carried !== undefined) return carried\n return placeholder === undefined ? undefined : String(placeholder)\n }\n return {\n signal: effectiveSignal,\n async prompt(prompt) {\n if (prompt.type === 'select') {\n const options = prompt.options ?? []\n pending = undefined\n const picked = await ui.select(prompt.message, options.map(option => option.label), effectiveSignal)\n return options.find(option => option.label === picked)?.id ?? picked\n }\n // BOTH signals close this box: the flow's own (another path won the race\n // — the local callback got the code) and the login's (it was called off).\n //\n // The second one is not optional. Cancelling a flow only tells the flow;\n // the question is the HOST's, and Pi's host takes its dialog down, which\n // is how the flow's `await manualPromise` gets to settle. Pi's anthropic\n // flow deadlocks without that: cancelling makes its callback wait return\n // nothing, and it then waits on the paste box forever, because the abort\n // that would close the box sits in a `finally` this wait never reaches.\n return ui.input(prompt.message, takePending(prompt.placeholder), joinSignals(effectiveSignal, prompt.signal))\n },\n notify(event) {\n if (event.type === 'auth_url') {\n // An authorize URL is 400 characters of query string. Pasted whole it\n // filled the dialog with wrapped text and gave the user nothing to\n // click. The dialog's detail slot is rendered as markdown, so the\n // address goes in as a LINK — one short clickable line, opened in a new\n // tab by the host's own renderer. The short redirect on this app's\n // origin is what makes it copyable too (a notice is plain text).\n const shown = (event.url === undefined ? undefined : shorten?.(event.url)) ?? event.url\n const tail = event.instructions === undefined ? '' : `\\n\\n${event.instructions}`\n pending = `[Open the login page](${shown})${tail}`\n ui.notify(`Open this URL to authorize: ${shown}${tail}`)\n // Open it, the way Pi's own login dialog does. The flows themselves\n // print \"A browser window should open\" — that sentence is Pi telling\n // the user what the HOST is about to do, and a host that does not do it\n // leaves a wrapped, unclickable URL and no way to authorize.\n openIfPossible(open, event.url)\n } else if (event.type === 'device_code') {\n // A device code EXPIRES. Dropping that left the user staring at a code\n // with no idea it was on a clock — every device-code flow sends the\n // window, and Pi's own host shows it.\n const window = typeof event.expiresInSeconds === 'number' && event.expiresInSeconds > 0\n ? ` (valid for ${Math.round(event.expiresInSeconds / 60)} min)`\n : ''\n // Same as above: a clickable line in the dialog, plain text in the\n // notice. The code stays as code so it survives the markdown pass.\n pending = `[Open the device login page](${event.verificationUri}) and enter code \\`${event.userCode}\\`${window}`\n ui.notify(`Visit ${event.verificationUri} and enter code ${event.userCode}${window}`)\n openIfPossible(open, event.verificationUri)\n } else if (event.message !== undefined) {\n ui.notify(event.message)\n }\n },\n }\n}\n\nfunction oauthConfigOf(providerConfig: UnknownRecord | undefined): UnknownRecord | undefined {\n // Extension-generation configs carry oauth at the top level; pi-ai\n // createProvider() objects carry it under auth.oauth. Pi accepts both.\n const oauth = providerConfig?.oauth\n ?? (providerConfig?.auth as UnknownRecord | undefined)?.oauth\n return typeof oauth === 'object' && oauth !== null && typeof (oauth as UnknownRecord).login === 'function'\n ? oauth as UnknownRecord\n : undefined\n}\n\n// Pi accepts both oauth generations: pi-ai native flows carry toAuth and take\n// the interaction surface directly; extension configs carry getApiKey and go\n// through Pi's adaptOAuth (vendored). Same acceptance here.\nfunction oauthAdapterOf(oauthConfig: UnknownRecord): {\n login(interaction: unknown): Promise<UnknownRecord>\n refresh(credential: unknown, signal?: AbortSignal): Promise<UnknownRecord>\n toAuth(credential: unknown): Promise<UnknownRecord>\n} {\n if (typeof oauthConfig.toAuth === 'function') {\n return oauthConfig as never\n }\n return adaptOAuth(oauthConfig) as never\n}\n\nexport function providerSupportsOAuth(providerConfig: UnknownRecord | undefined): boolean {\n return oauthConfigOf(providerConfig) !== undefined\n}\n\n// Pi models.login semantics: find the provider's oauth surface, run the\n// package's own login through the vendored adapter, persist through the\n// store's serialized modify.\nexport async function loginPiProvider(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n ui: OAuthUiSurface\n signal?: AbortSignal\n /** Turns a long authorize URL into a short one the user can actually click. */\n shorten?: (url: string) => string | undefined\n /** The host's browser opener. Absent = this host opens nothing. */\n open?: (url: string) => void\n}): Promise<UnknownRecord> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n if (oauthConfig === undefined) {\n throw new Error(`${options.providerName ?? options.providerId} does not support oauth login`)\n }\n const adapter = oauthAdapterOf(oauthConfig)\n // The flow's own signal, so a question still on screen when the login ends\n // gets cancelled. The browser half of an OAuth login finishes at the local\n // callback, not at the paste box — leaving that box open after the flow has\n // resolved (or failed, as a region-blocked token exchange does) strands the\n // user in front of a dialog that can no longer do anything.\n const done = new AbortController()\n if (options.signal !== undefined) {\n if (options.signal.aborted) done.abort()\n else options.signal.addEventListener('abort', () => done.abort(), { once: true })\n }\n try {\n const credential = await adapter.login(oauthInteraction(options.ui, done.signal, options.shorten, options.open))\n await options.store.modify(options.providerId, async () => credential)\n return credential\n } finally {\n done.abort()\n }\n}\n\n// Pi's resolveProviderAuth over the stored credential: five-minute expiry\n// window, double-checked-lock refresh through the package's own refreshToken,\n// rotated credential persisted before release, key derived by the package's\n// own getApiKey. Returns undefined when nothing is stored.\nexport async function resolveOAuthApiKey(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n signal?: AbortSignal\n}): Promise<string | undefined> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n if (oauthConfig === undefined) return undefined\n const provider = {\n id: options.providerId,\n name: options.providerName ?? options.providerId,\n auth: { oauth: oauthAdapterOf(oauthConfig) },\n }\n const resolved = await resolveProviderAuth(\n provider,\n options.store,\n { env: async () => undefined },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n ) as { auth?: { apiKey?: string } } | undefined\n return resolved?.auth?.apiKey\n}\n\nexport async function storedOAuthCredential(store: FileCredentialStore, providerId: string): Promise<UnknownRecord | undefined> {\n const stored = await store.read(providerId, undefined) as UnknownRecord | undefined\n return stored?.type === 'oauth' ? stored : undefined\n}\n\n// Pi's FULL getProviderAuth precedence over the vendored resolver — stored\n// OAuth (double-checked-lock refresh), then stored api_key, then the\n// provider's own auth.apiKey ambient resolution (its resolve() reads env vars\n// and probes credential files through the auth context). The package's\n// resolve code runs verbatim; the bridge supplies only the context.\nexport async function resolvePiProviderAuth(options: {\n providerId: string\n providerName?: string\n providerConfig: UnknownRecord\n store: FileCredentialStore\n signal?: AbortSignal\n}): Promise<{ auth?: UnknownRecord, source?: string } | undefined> {\n const oauthConfig = oauthConfigOf(options.providerConfig)\n const declaredApiKey = (options.providerConfig.auth as UnknownRecord | undefined)?.apiKey\n if (oauthConfig === undefined && declaredApiKey === undefined) return undefined\n const auth: UnknownRecord = {}\n if (oauthConfig !== undefined) auth.oauth = oauthAdapterOf(oauthConfig)\n if (declaredApiKey !== undefined) auth.apiKey = declaredApiKey\n const provider = { id: options.providerId, name: options.providerName ?? options.providerId, auth }\n return await resolveProviderAuth(\n provider,\n options.store,\n {\n env: async (name: string) => process.env[name],\n fileExists: async (path: string) => existsSync(path),\n },\n options.signal !== undefined ? { signal: options.signal } : undefined,\n ) as { auth?: UnknownRecord, source?: string } | undefined\n}\n","import { join } from 'node:path'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { CredentialProvider, type CredentialInfo, type CredentialRef, type ResolvedCredential } from '@deepseek-ai/dsh-credentials'\nimport { FileCredentialStore, providerSupportsOAuth, resolveOAuthApiKey, storedOAuthCredential } from './oauth-bridge.js'\nimport { builtinProviders } from './compat/pi-ai.js'\nimport { getAgentDir } from './compat/pi-coding-agent.js'\n\ntype UnknownRecord = Record<string, unknown>\n\n// L4 of the OAuth host seam: a standard dsh-credentials provider that serves\n// Pi OAuth tokens to DSH's native LLM request path. A route configured as\n// `apiKeyEnv: PI2DSH_OAUTH_OPENAI_CODEX` resolves per request through this\n// provider — Pi's double-checked-lock refresh runs on every resolution, so a\n// rotated token reaches the next model call with no restart, exactly the\n// per-operation semantics the credentials seam demands. Every other reference\n// falls through to the process environment, so one provider instance serves a\n// whole composition.\n//\n// Reference convention: PI2DSH_OAUTH_<PROVIDER_ID> where the provider id is\n// upper-cased with `-` as `_` (openai-codex → PI2DSH_OAUTH_OPENAI_CODEX).\n\nconst OAUTH_REF_PREFIX = 'PI2DSH_OAUTH_'\n\nexport function oauthCredentialRef(providerId: string): string {\n return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll('-', '_')}`\n}\n\nfunction providerIdOfRef(ref: string): string | undefined {\n if (!ref.startsWith(OAUTH_REF_PREFIX)) return undefined\n return ref.slice(OAUTH_REF_PREFIX.length).toLowerCase().replaceAll('_', '-')\n}\n\nexport interface PiOAuthCredentialProviderOptions {\n /** Path to the Pi-format auth.json; defaults to `$agentDir/auth.json`. */\n authPath?: string\n /** Additional provider configs (id → config with an oauth block), e.g. from packages that registered providers. */\n providers?: ReadonlyMap<string, UnknownRecord>\n}\n\nexport class PiOAuthCredentialProvider extends CredentialProvider {\n private readonly oauthStore: FileCredentialStore\n private readonly extraProviders: ReadonlyMap<string, UnknownRecord>\n\n constructor(ctx: Context, options: PiOAuthCredentialProviderOptions = {}) {\n super(ctx)\n this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), 'auth.json'))\n this.extraProviders = options.providers ?? new Map()\n }\n\n private oauthConfigFor(providerId: string): UnknownRecord | undefined {\n const registered = this.extraProviders.get(providerId)\n if (providerSupportsOAuth(registered)) return registered\n const builtin = builtinProviders().find(provider => provider.id === providerId)\n if (builtin === undefined) return undefined\n return { name: builtin.name, baseUrl: builtin.baseUrl, oauth: builtin.auth.oauth }\n }\n\n async resolve(ref: CredentialRef): Promise<ResolvedCredential | undefined> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return value !== undefined && value.length > 0 ? { value, source: 'env' } : undefined\n }\n const config = this.oauthConfigFor(providerId)\n if (config === undefined) return undefined\n const value = await resolveOAuthApiKey({\n providerId,\n providerName: typeof config.name === 'string' ? config.name : providerId,\n providerConfig: config,\n store: this.oauthStore,\n })\n return value !== undefined && value.length > 0 ? { value, source: 'pi-oauth' } : undefined\n }\n\n async describe(ref: CredentialRef): Promise<CredentialInfo> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n const value = process.env[String(ref)]\n return { configured: value !== undefined && value.length > 0, ...(value ? { source: 'env' } : {}), writable: false }\n }\n const stored = await storedOAuthCredential(this.oauthStore, providerId)\n return { configured: stored !== undefined, ...(stored !== undefined ? { source: 'pi-oauth' } : {}), writable: false }\n }\n\n async set(ref: CredentialRef, _value: string): Promise<void> {\n throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`)\n }\n\n async unset(ref: CredentialRef): Promise<void> {\n const providerId = providerIdOfRef(String(ref))\n if (providerId === undefined) {\n throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`)\n }\n // Logging out is a legitimate unset: drop the stored token.\n await this.oauthStore.delete(providerId, undefined)\n }\n}\n\nexport const name = 'pi2dsh-credentials-oauth'\n\nexport function apply(ctx: Context, config: PiOAuthCredentialProviderOptions = {}): void {\n // The Service constructor registers itself as ctx.credentials.\n void new PiOAuthCredentialProvider(ctx, config)\n}\n"],"mappings":";;;;;;;;;AACA,SAAS,YAAY,QAAQ;CACzB,IAAI,OAAO,WAAW,KAAA,GAClB,OAAO,OAAO;CAClB,MAAM,wBAAQ,IAAI,MAAM,2BAA2B;CACnD,MAAM,OAAO;CACb,OAAO;AACX;;AAEA,SAAgB,gBAAgB,QAAQ;CACpC,OAAO,UAAU,IAAI,gBAAgB,CAAC,CAAC;AAC3C;;;;;AAKA,SAAgB,oBAAoB,WAAW,QAAQ;CACnD,IAAI,OAAO,SAAS;EAChB,UAAe,YAAY,CAAE,CAAC;EAC9B,OAAO,QAAQ,OAAO,YAAY,MAAM,CAAC;CAC7C;CACA,OAAO,IAAI,SAAS,SAAS,WAAW;EACpC,IAAI,UAAU;EACd,MAAM,gBAAgB,OAAO,oBAAoB,SAAS,OAAO;EACjE,MAAM,gBAAgB;GAClB,IAAI,SACA;GACJ,UAAU;GACV,QAAQ;GACR,OAAO,YAAY,MAAM,CAAC;EAC9B;EACA,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;EACxD,UAAe,MAAM,UAAU;GAC3B,IAAI,SACA;GACJ,UAAU;GACV,QAAQ;GACR,QAAQ,KAAK;EACjB,IAAI,UAAU;GACV,IAAI,SACA;GACJ,UAAU;GACV,QAAQ;GACR,OAAO,KAAK;EAChB,CAAC;EACD,IAAI,OAAO,SACP,QAAQ;CAChB,CAAC;AACL;;;;;;;;ACzCA,IAAa,0BAAb,MAAqC;CACjC,8BAAc,IAAI,IAAI;CACtB,yBAAS,IAAI,IAAI;;CAEjB,QAAQ,YAAY,MAAM,SAAS;EAC/B,MAAM,SAAS,gBAAgB,SAAS,MAAM;EAC9C,MAAM,WAAW,KAAK,OAAO,IAAI,UAAU,KAAK,QAAQ,QAAQ;EAChE,MAAM,UAAU,YAAY;GACxB,MAAM,SAAS,YAAY,CAAE,CAAC;GAC9B,OAAO,eAAe;GACtB,OAAO,KAAK;EAChB,EAAA,CAAG;EACH,MAAM,OAAO,OAAO,YAAY,CAAE,CAAC;EACnC,KAAK,OAAO,IAAI,YAAY,IAAI;EAChC,KAAU,WAAW;GACjB,IAAI,KAAK,OAAO,IAAI,UAAU,MAAM,MAChC,KAAK,OAAO,OAAO,UAAU;EACrC,CAAC;EACD,OAAO,oBAAoB,QAAQ,MAAM;CAC7C;CACA,MAAM,KAAK,YAAY,SAAS;EAC5B,SAAS,QAAQ,eAAe;EAChC,OAAO,KAAK,YAAY,IAAI,UAAU;CAC1C;CACA,MAAM,KAAK,SAAS;EAChB,SAAS,QAAQ,eAAe;EAChC,OAAO,CAAC,GAAG,KAAK,WAAW,CAAC,CAAC,KAAK,CAAC,YAAY,iBAAiB;GAAE;GAAY,MAAM,WAAW;EAAK,EAAE;CAC1G;CACA,OAAO,YAAY,IAAI,SAAS;EAC5B,OAAO,KAAK,QAAQ,YAAY,YAAY;GACxC,MAAM,UAAU,KAAK,YAAY,IAAI,UAAU;GAC/C,MAAM,OAAO,MAAM,GAAG,OAAO;GAC7B,SAAS,QAAQ,eAAe;GAChC,IAAI,SAAS,KAAA,GACT,KAAK,YAAY,IAAI,YAAY,IAAI;GACzC,OAAO,QAAQ;EACnB,GAAG,OAAO;CACd;CACA,OAAO,YAAY,SAAS;EACxB,OAAO,KAAK,QAAQ,YAAY,YAAY;GACxC,KAAK,YAAY,OAAO,UAAU;EACtC,GAAG,OAAO;CACd;AACJ;;;ACjDA,SAAgB,kBAAkB,OAAO;CACrC,IAAI,iBAAiB,OACjB,OAAO,MAAM,WAAW,MAAM;CAClC,IAAI,OAAO,UAAU,UACjB,OAAO;CACX,OAAO,OAAO,KAAK;AACvB;;;ACHA,IAAa,cAAb,cAAiC,MAAM;CACnC;CACA,YAAY,MAAM,SAAS,SAAS;EAChC,MAAM,gBAAgB,SAAS,SAAS,KAAK,GAAG,OAAO;EACvD,KAAK,OAAO;EACZ,KAAK,OAAO;CAChB;AACJ;;AAEA,SAAS,gBAAgB,SAAS,OAAO;CACrC,IAAI,UAAU,KAAA,KAAa,UAAU,MACjC,OAAO;CACX,MAAM,SAAS,kBAAkB,KAAK,CAAC,CAAC,KAAK;CAC7C,IAAI,CAAC,UAAU,QAAQ,SAAS,MAAM,GAClC,OAAO;CACX,OAAO,GAAG,QAAQ,IAAI;AAC1B;;;;;;;AAOA,SAAgB,oBAAoB,UAAU,aAAa,aAAa,WAAW;CAC/E,MAAM,SAAS,gBAAgB,WAAW,MAAM;CAChD,OAAO,oBAAoB,8BAA8B,UAAU,aAAa,aAAa,WAAW,MAAM,GAAG,MAAM;AAC3H;AACA,eAAe,8BAA8B,UAAU,aAAa,aAAa,WAAW,QAAQ;CAChG,OAAO,eAAe;CACtB,MAAM,qBAAqB,WAAW,MAAM,sBAAsB,aAAa,UAAU,GAAG,IAAI;CAChG,IAAI,WAAW,WAAW,KAAA,KAAa,SAAS,KAAK,QACjD,OAAO,cAAc,oBAAoB,SAAS,KAAK,QAAQ,SAAS,IAAI;EACxE,MAAM;EACN,KAAK,UAAU;EACf,KAAK,UAAU;CACnB,GAAG,MAAM;CAEb,MAAM,SAAS,MAAM,eAAe,aAAa,SAAS,IAAI,MAAM;CACpE,IAAI,QAAQ;EACR,IAAI,OAAO,SAAS,WAAW,SAAS,KAAK,OACzC,OAAO,mBAAmB,aAAa,SAAS,IAAI,SAAS,KAAK,OAAO,QAAQ,QAAQ,WAAW,kBAAkB;EAE1H,IAAI,OAAO,SAAS,aAAa,SAAS,KAAK,QAAQ;GACnD,MAAM,aAAa,WAAW,MAAM;IAAE,GAAG;IAAQ,KAAK;KAAE,GAAG,OAAO;KAAK,GAAG,UAAU;IAAI;GAAE,IAAI;GAC9F,OAAO,cAAc,oBAAoB,SAAS,KAAK,QAAQ,SAAS,IAAI,YAAY,MAAM;EAClG;EACA;CACJ;CAEA,OAAO,SAAS,KAAK,SACf,cAAc,oBAAoB,SAAS,KAAK,QAAQ,SAAS,IAAI,KAAA,GAAW,MAAM,IACtF,KAAA;AACV;AACA,SAAS,sBAAsB,MAAM,KAAK;CACtC,OAAO;EACH,KAAK,OAAO,SAAS,IAAI,SAAU,MAAM,KAAK,IAAI,IAAI;EACtD,aAAa,SAAS,KAAK,WAAW,IAAI;CAC9C;AACJ;AACA,MAAM,oCAAoC;AAC1C,MAAM,mCAAmC;;;;;;AAMzC,eAAe,mBAAmB,aAAa,YAAY,OAAO,QAAQ,QAAQ,oBAAoB;CAClG,MAAM,oBAAoB,KAAK,IAAI,mCAAmC,sBAAsB,CAAC;CAC7F,MAAM,eAAe,eAAe,KAAK,IAAI,IAAI,qBAAqB,WAAW;CACjF,IAAI,aAAa;CACjB,IAAI,YAAY,UAAU,GAAG;EAEzB,IAAI;EACJ,IAAI;GACA,OAAO,MAAM,YAAY,OAAO,YAAY,OAAO,YAAY;IAC3D,IAAI,SAAS,SAAS,SAClB,OAAO,KAAA;IACX,IAAI,CAAC,YAAY,OAAO,GACpB,OAAO,KAAA;IACX,IAAI;KACA,MAAM,gBAAgB,YAAY,IAAI,CAClC,QACA,YAAY,QAAQ,gCAAgC,CACxD,CAAC;KACD,OAAO,MAAM,MAAM,QAAQ,SAAS,aAAa;IACrD,SACO,OAAO;KACV,MAAM,IAAI,YAAY,SAAS,4BAA4B,cAAc,EAAE,OAAO,MAAM,CAAC;IAC7F;GACJ,GAAG,EAAE,OAAO,CAAC;EACjB,SACO,OAAO;GACV,IAAI,iBAAiB,aACjB,MAAM;GACV,MAAM,IAAI,YAAY,QAAQ,sCAAsC,cAAc,EAAE,OAAO,MAAM,CAAC;EACtG;EACA,IAAI,MAAM,SAAS,SACf,OAAO,KAAA;EACX,aAAa;EAIb,IAAI,uBAAuB,KAAA,KAAa,YAAY,UAAU,GAC1D,MAAM,IAAI,YAAY,SAAS,4DAA4D,YAAY;CAE/G;CACA,IAAI;EACA,OAAO;GAAE,MAAM,MAAM,MAAM,OAAO,UAAU;GAAG,QAAQ;EAAQ;CACnE,SACO,OAAO;EACV,MAAM,IAAI,YAAY,SAAS,oCAAoC,cAAc,EAAE,OAAO,MAAM,CAAC;CACrG;AACJ;AACA,eAAe,cAAc,aAAa,QAAQ,YAAY,YAAY,QAAQ;CAC9E,IAAI;EACA,OAAO,MAAM,OAAO,QAAQ;GAAE,KAAK;GAAa;GAAY;EAAO,CAAC;CACxE,SACO,OAAO;EACV,MAAM,IAAI,YAAY,QAAQ,oCAAoC,cAAc,EAAE,OAAO,MAAM,CAAC;CACpG;AACJ;AACA,eAAe,eAAe,aAAa,YAAY,QAAQ;CAC3D,IAAI;EACA,OAAO,MAAM,YAAY,KAAK,YAAY,EAAE,OAAO,CAAC;CACxD,SACO,OAAO;EACV,MAAM,IAAI,YAAY,QAAQ,oCAAoC,cAAc,EAAE,OAAO,MAAM,CAAC;CACpG;AACJ;;;ACjIA,SAAgB,WAAW,QAAQ;CAC/B,OAAO;EACH,MAAM,OAAO;EACb,gBAAgB,OAAO;EACvB,OAAO,OAAO,cAAc;GAUxB,OAAO;IAAE,GAAG,MATa,OAAO,MAAM;KAClC,SAAS,SAAS,UAAU,OAAO;MAAE,MAAM;MAAY,GAAG;KAAK,CAAC;KAChE,eAAe,SAAS,UAAU,OAAO;MAAE,MAAM;MAAe,GAAG;KAAK,CAAC;KACzE,WAAW,WAAW,UAAU,OAAO;MAAE,MAAM;MAAQ,GAAG;KAAO,CAAC;KAClE,aAAa,YAAY,UAAU,OAAO;MAAE,MAAM;MAAY;KAAQ,CAAC;KACvE,yBAAyB,UAAU,OAAO;MAAE,MAAM;MAAe,SAAS;KAA+B,CAAC;KAC1G,WAAW,WAAW,UAAU,OAAO;MAAE,MAAM;MAAU,GAAG;KAAO,CAAC;KACpE,QAAQ,UAAU;IACtB,CAAC;IACuB,MAAM;GAAQ;EAC1C;EACA,SAAS,OAAO,YAAY,YAAY;GAAE,GAAI,MAAM,OAAO,aAAa,YAAY,MAAM;GAAI,MAAM;EAAQ;EAC5G,QAAQ,OAAO,gBAAgB,EAAE,QAAQ,OAAO,UAAU,UAAU,EAAE;CAC1E;AACJ;;;ACEA,IAAa,sBAAb,cAAyC,wBAAwB;CAC/D;CAEA,YAAY,MAAc;EACxB,MAAM;EACN,KAAKA,QAAQ;EACb,IAAI;GACF,MAAM,OAAO,KAAK,MAAM,aAAa,MAAM,MAAM,CAAC;GAClD,KAAK,MAAM,CAAC,YAAY,eAAe,OAAO,QAAQ,IAAI,GACvD,KAA2D,YAAY,IAAI,YAAY,UAAU;EAEtG,QAAQ,CAER;CACF;CAEA,IAAI,OAAe;EACjB,OAAO,KAAKA;CACd;CAEA,MAAMC,WAA0B;EAC9B,MAAM,cAAe,KAA0D;EAC/E,MAAM,OAAO,OAAO,YAAY,WAAW;EAC3C,MAAM,MAAM,QAAQ,KAAKD,KAAK,GAAG,EAAE,WAAW,KAAK,CAAC;EAGpD,MAAM,OAAO,GAAG,KAAKA,MAAM,OAAO,QAAQ,IAAI,GAAG,KAAK,MAAM,KAAK,OAAO,IAAI,GAAG,CAAC,CAAC,SAAS,EAAE;EAC5F,MAAM,UAAU,MAAM,GAAG,KAAK,UAAU,MAAM,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,IAAM,CAAC;EAC3E,MAAM,OAAO,MAAM,KAAKA,KAAK;CAC/B;CAEA,OAAgB,YAAoB,IAAmC,SAA2C;EAGhH,OAAO,MAAM,OAAO,YAAY,OAAO,YAAqB;GAC1D,MAAM,OAAO,MAAM,GAAG,OAAO;GAC7B,IAAI,SAAS,KAAA,GAAW;IAEtB,KAD+E,YACnE,IAAI,YAAY,IAAI;IAChC,MAAM,KAAKC,SAAS;GACtB;GACA,OAAO;EACT,GAAG,OAAO;CACZ;CAEA,OAAgB,YAAoB,SAAwC;EAC1E,OAAO,MAAM,OAAO,YAAY,OAAO,CAAC,CAAC,WAAW,KAAKA,SAAS,CAAC;CACrE;AACF;;;;;;;;;;AAWA,SAAgB,YAAY,SAAkC,SAA2C;CACvG,IAAI,EAAE,mBAAmB,cAAc,OAAO;CAC9C,IAAI,YAAY,KAAA,GAAW,OAAO;CAClC,MAAM,SAAS,IAAI,gBAAgB;CACnC,MAAM,aAAa,OAAO,MAAM;CAChC,IAAI,QAAQ,WAAW,QAAQ,SAAS,OAAO,MAAM;MAChD;EACH,QAAQ,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;EACtD,QAAQ,iBAAiB,SAAS,MAAM,EAAE,MAAM,KAAK,CAAC;CACxD;CACA,OAAO,OAAO;AAChB;;;;;;;;;;;AA+CA,SAAS,eAAe,MAA2C,KAA+B;CAChG,IAAI,SAAS,KAAA,KAAa,OAAO,QAAQ,YAAY,CAAC,gBAAgB,KAAK,GAAG,GAAG;CACjF,IAAI;EACF,KAAK,GAAG;CACV,QAAQ,CAIR;AACF;AAEA,SAAgB,iBACd,IACA,QACA,SACA,MAKA;CAGA,MAAM,kBAAkB,UAAU,IAAI,gBAAgB,CAAC,CAAC;CAUxD,IAAI;;;;;;;;;;CAUJ,MAAM,eAAe,gBAA6C;EAChE,MAAM,UAAU;EAChB,UAAU,KAAA;EAQV,IAAI,YAAY,KAAA,GAAW,OAAO;EAClC,OAAO,gBAAgB,KAAA,IAAY,KAAA,IAAY,OAAO,WAAW;CACnE;CACA,OAAO;EACL,QAAQ;EACR,MAAM,OAAO,QAAQ;GACnB,IAAI,OAAO,SAAS,UAAU;IAC5B,MAAM,UAAU,OAAO,WAAW,CAAC;IACnC,UAAU,KAAA;IACV,MAAM,SAAS,MAAM,GAAG,OAAO,OAAO,SAAS,QAAQ,KAAI,WAAU,OAAO,KAAK,GAAG,eAAe;IACnG,OAAO,QAAQ,MAAK,WAAU,OAAO,UAAU,MAAM,CAAC,EAAE,MAAM;GAChE;GAUA,OAAO,GAAG,MAAM,OAAO,SAAS,YAAY,OAAO,WAAW,GAAG,YAAY,iBAAiB,OAAO,MAAM,CAAC;EAC9G;EACA,OAAO,OAAO;GACZ,IAAI,MAAM,SAAS,YAAY;IAO7B,MAAM,SAAS,MAAM,QAAQ,KAAA,IAAY,KAAA,IAAY,UAAU,MAAM,GAAG,MAAM,MAAM;IACpF,MAAM,OAAO,MAAM,iBAAiB,KAAA,IAAY,KAAK,OAAO,MAAM;IAClE,UAAU,yBAAyB,MAAM,GAAG;IAC5C,GAAG,OAAO,+BAA+B,QAAQ,MAAM;IAKvD,eAAe,MAAM,MAAM,GAAG;GAChC,OAAO,IAAI,MAAM,SAAS,eAAe;IAIvC,MAAM,SAAS,OAAO,MAAM,qBAAqB,YAAY,MAAM,mBAAmB,IAClF,eAAe,KAAK,MAAM,MAAM,mBAAmB,EAAE,EAAE,SACvD;IAGJ,UAAU,gCAAgC,MAAM,gBAAgB,qBAAqB,MAAM,SAAS,IAAI;IACxG,GAAG,OAAO,SAAS,MAAM,gBAAgB,kBAAkB,MAAM,WAAW,QAAQ;IACpF,eAAe,MAAM,MAAM,eAAe;GAC5C,OAAO,IAAI,MAAM,YAAY,KAAA,GAC3B,GAAG,OAAO,MAAM,OAAO;EAE3B;CACF;AACF;AAEA,SAAS,cAAc,gBAAsE;CAG3F,MAAM,QAAQ,gBAAgB,UACxB,gBAAgB,KAAA,EAAoC;CAC1D,OAAO,OAAO,UAAU,YAAY,UAAU,QAAQ,OAAQ,MAAwB,UAAU,aAC5F,QACA,KAAA;AACN;AAKA,SAAS,eAAe,aAItB;CACA,IAAI,OAAO,YAAY,WAAW,YAChC,OAAO;CAET,OAAO,WAAW,WAAW;AAC/B;AAEA,SAAgB,sBAAsB,gBAAoD;CACxF,OAAO,cAAc,cAAc,MAAM,KAAA;AAC3C;AAKA,eAAsB,gBAAgB,SAWX;CACzB,MAAM,cAAc,cAAc,QAAQ,cAAc;CACxD,IAAI,gBAAgB,KAAA,GAClB,MAAM,IAAI,MAAM,GAAG,QAAQ,gBAAgB,QAAQ,WAAW,8BAA8B;CAE9F,MAAM,UAAU,eAAe,WAAW;CAM1C,MAAM,OAAO,IAAI,gBAAgB;CACjC,IAAI,QAAQ,WAAW,KAAA,GAAW;EAChC,IAAI,QAAQ,OAAO,SAAS,KAAK,MAAM;OAClC,QAAQ,OAAO,iBAAiB,eAAe,KAAK,MAAM,GAAG,EAAE,MAAM,KAAK,CAAC;CAClF;CACA,IAAI;EACF,MAAM,aAAa,MAAM,QAAQ,MAAM,iBAAiB,QAAQ,IAAI,KAAK,QAAQ,QAAQ,SAAS,QAAQ,IAAI,CAAC;EAC/G,MAAM,QAAQ,MAAM,OAAO,QAAQ,YAAY,YAAY,UAAU;EACrE,OAAO;CACT,UAAU;EACR,KAAK,MAAM;CACb;AACF;AAMA,eAAsB,mBAAmB,SAMT;CAC9B,MAAM,cAAc,cAAc,QAAQ,cAAc;CACxD,IAAI,gBAAgB,KAAA,GAAW,OAAO,KAAA;CAYtC,QAAO,MANgB,oBACrB;EALA,IAAI,QAAQ;EACZ,MAAM,QAAQ,gBAAgB,QAAQ;EACtC,MAAM,EAAE,OAAO,eAAe,WAAW,EAAE;CAG3C,GACA,QAAQ,OACR,EAAE,KAAK,YAAY,KAAA,EAAU,GAC7B,QAAQ,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,KAAA,CAC9D,EAAA,EACiB,MAAM;AACzB;AAEA,eAAsB,sBAAsB,OAA4B,YAAwD;CAC9H,MAAM,SAAS,MAAM,MAAM,KAAK,YAAY,KAAA,CAAS;CACrD,OAAO,QAAQ,SAAS,UAAU,SAAS,KAAA;AAC7C;AAOA,eAAsB,sBAAsB,SAMuB;CACjE,MAAM,cAAc,cAAc,QAAQ,cAAc;CACxD,MAAM,iBAAkB,QAAQ,eAAe,MAAoC;CACnF,IAAI,gBAAgB,KAAA,KAAa,mBAAmB,KAAA,GAAW,OAAO,KAAA;CACtE,MAAM,OAAsB,CAAC;CAC7B,IAAI,gBAAgB,KAAA,GAAW,KAAK,QAAQ,eAAe,WAAW;CACtE,IAAI,mBAAmB,KAAA,GAAW,KAAK,SAAS;CAEhD,OAAO,MAAM,oBACX;EAFiB,IAAI,QAAQ;EAAY,MAAM,QAAQ,gBAAgB,QAAQ;EAAY;CAE3F,GACA,QAAQ,OACR;EACE,KAAK,OAAO,SAAiB,QAAQ,IAAI;EACzC,YAAY,OAAO,SAAiB,WAAW,IAAI;CACrD,GACA,QAAQ,WAAW,KAAA,IAAY,EAAE,QAAQ,QAAQ,OAAO,IAAI,KAAA,CAC9D;AACF;;;ACvWA,MAAM,mBAAmB;AAEzB,SAAgB,mBAAmB,YAA4B;CAC7D,OAAO,GAAG,mBAAmB,WAAW,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG;AAC3E;AAEA,SAAS,gBAAgB,KAAiC;CACxD,IAAI,CAAC,IAAI,WAAW,gBAAgB,GAAG,OAAO,KAAA;CAC9C,OAAO,IAAI,MAAM,EAAuB,CAAC,CAAC,YAAY,CAAC,CAAC,WAAW,KAAK,GAAG;AAC7E;AASA,IAAa,4BAAb,cAA+C,mBAAmB;CAChE;CACA;CAEA,YAAY,KAAc,UAA4C,CAAC,GAAG;EACxE,MAAM,GAAG;EACT,KAAK,aAAa,IAAI,oBAAoB,QAAQ,YAAY,KAAK,YAAY,GAAG,WAAW,CAAC;EAC9F,KAAK,iBAAiB,QAAQ,6BAAa,IAAI,IAAI;CACrD;CAEA,eAAuB,YAA+C;EACpE,MAAM,aAAa,KAAK,eAAe,IAAI,UAAU;EACrD,IAAI,sBAAsB,UAAU,GAAG,OAAO;EAC9C,MAAM,UAAU,iBAAiB,CAAC,CAAC,MAAK,aAAY,SAAS,OAAO,UAAU;EAC9E,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;EAClC,OAAO;GAAE,MAAM,QAAQ;GAAM,SAAS,QAAQ;GAAS,OAAO,QAAQ,KAAK;EAAM;CACnF;CAEA,MAAM,QAAQ,KAA6D;EACzE,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC;EAC9C,IAAI,eAAe,KAAA,GAAW;GAC5B,MAAM,QAAQ,QAAQ,IAAI,OAAO,GAAG;GACpC,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,IAAI;IAAE;IAAO,QAAQ;GAAM,IAAI,KAAA;EAC9E;EACA,MAAM,SAAS,KAAK,eAAe,UAAU;EAC7C,IAAI,WAAW,KAAA,GAAW,OAAO,KAAA;EACjC,MAAM,QAAQ,MAAM,mBAAmB;GACrC;GACA,cAAc,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;GAC9D,gBAAgB;GAChB,OAAO,KAAK;EACd,CAAC;EACD,OAAO,UAAU,KAAA,KAAa,MAAM,SAAS,IAAI;GAAE;GAAO,QAAQ;EAAW,IAAI,KAAA;CACnF;CAEA,MAAM,SAAS,KAA6C;EAC1D,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC;EAC9C,IAAI,eAAe,KAAA,GAAW;GAC5B,MAAM,QAAQ,QAAQ,IAAI,OAAO,GAAG;GACpC,OAAO;IAAE,YAAY,UAAU,KAAA,KAAa,MAAM,SAAS;IAAG,GAAI,QAAQ,EAAE,QAAQ,MAAM,IAAI,CAAC;IAAI,UAAU;GAAM;EACrH;EACA,MAAM,SAAS,MAAM,sBAAsB,KAAK,YAAY,UAAU;EACtE,OAAO;GAAE,YAAY,WAAW,KAAA;GAAW,GAAI,WAAW,KAAA,IAAY,EAAE,QAAQ,WAAW,IAAI,CAAC;GAAI,UAAU;EAAM;CACtH;CAEA,MAAM,IAAI,KAAoB,QAA+B;EAC3D,MAAM,IAAI,MAAM,cAAc,OAAO,GAAG,EAAE,wFAAwF;CACpI;CAEA,MAAM,MAAM,KAAmC;EAC7C,MAAM,aAAa,gBAAgB,OAAO,GAAG,CAAC;EAC9C,IAAI,eAAe,KAAA,GACjB,MAAM,IAAI,MAAM,cAAc,OAAO,GAAG,EAAE,gEAAgE;EAG5G,MAAM,KAAK,WAAW,OAAO,YAAY,KAAA,CAAS;CACpD;AACF;AAEA,MAAa,OAAO;AAEpB,SAAgB,MAAM,KAAc,SAA2C,CAAC,GAAS;CAEvF,IAAS,0BAA0B,KAAK,MAAM;AAChD"}
@@ -1,91 +1,3 @@
1
1
 
2
- import { a as builtinProviders } from "./pi-ai-Dyg4zyLZ.mjs";
3
- import "./pi-coding-agent-CYnmeJEx.mjs";
4
- import { t as getAgentDir } from "./pi-config-shim-CZ1wFzqM.mjs";
5
- import { i as resolveOAuthApiKey, o as storedOAuthCredential, r as providerSupportsOAuth, t as FileCredentialStore } from "./oauth-bridge-CfyImWx4.mjs";
6
- import { join } from "node:path";
7
- import { CredentialProvider } from "@deepseek-ai/dsh-credentials";
8
- //#region src/credentials-oauth.ts
9
- const OAUTH_REF_PREFIX = "PI2DSH_OAUTH_";
10
- function oauthCredentialRef(providerId) {
11
- return `${OAUTH_REF_PREFIX}${providerId.toUpperCase().replaceAll("-", "_")}`;
12
- }
13
- function providerIdOfRef(ref) {
14
- if (!ref.startsWith(OAUTH_REF_PREFIX)) return void 0;
15
- return ref.slice(13).toLowerCase().replaceAll("_", "-");
16
- }
17
- var PiOAuthCredentialProvider = class extends CredentialProvider {
18
- oauthStore;
19
- extraProviders;
20
- constructor(ctx, options = {}) {
21
- super(ctx);
22
- this.oauthStore = new FileCredentialStore(options.authPath ?? join(getAgentDir(), "auth.json"));
23
- this.extraProviders = options.providers ?? /* @__PURE__ */ new Map();
24
- }
25
- oauthConfigFor(providerId) {
26
- const registered = this.extraProviders.get(providerId);
27
- if (providerSupportsOAuth(registered)) return registered;
28
- const builtin = builtinProviders().find((provider) => provider.id === providerId);
29
- if (builtin === void 0) return void 0;
30
- return {
31
- name: builtin.name,
32
- baseUrl: builtin.baseUrl,
33
- oauth: builtin.auth.oauth
34
- };
35
- }
36
- async resolve(ref) {
37
- const providerId = providerIdOfRef(String(ref));
38
- if (providerId === void 0) {
39
- const value = process.env[String(ref)];
40
- return value !== void 0 && value.length > 0 ? {
41
- value,
42
- source: "env"
43
- } : void 0;
44
- }
45
- const config = this.oauthConfigFor(providerId);
46
- if (config === void 0) return void 0;
47
- const value = await resolveOAuthApiKey({
48
- providerId,
49
- providerName: typeof config.name === "string" ? config.name : providerId,
50
- providerConfig: config,
51
- store: this.oauthStore
52
- });
53
- return value !== void 0 && value.length > 0 ? {
54
- value,
55
- source: "pi-oauth"
56
- } : void 0;
57
- }
58
- async describe(ref) {
59
- const providerId = providerIdOfRef(String(ref));
60
- if (providerId === void 0) {
61
- const value = process.env[String(ref)];
62
- return {
63
- configured: value !== void 0 && value.length > 0,
64
- ...value ? { source: "env" } : {},
65
- writable: false
66
- };
67
- }
68
- const stored = await storedOAuthCredential(this.oauthStore, providerId);
69
- return {
70
- configured: stored !== void 0,
71
- ...stored !== void 0 ? { source: "pi-oauth" } : {},
72
- writable: false
73
- };
74
- }
75
- async set(ref, _value) {
76
- throw new Error(`credential ${String(ref)} is read-only here: OAuth tokens are managed by /login, environment values by the shell`);
77
- }
78
- async unset(ref) {
79
- const providerId = providerIdOfRef(String(ref));
80
- if (providerId === void 0) throw new Error(`credential ${String(ref)} is read-only here: environment values are managed by the shell`);
81
- await this.oauthStore.delete(providerId, void 0);
82
- }
83
- };
84
- const name = "pi2dsh-credentials-oauth";
85
- function apply(ctx, config = {}) {
86
- new PiOAuthCredentialProvider(ctx, config);
87
- }
88
- //#endregion
2
+ import { i as oauthCredentialRef, n as apply, r as name, t as PiOAuthCredentialProvider } from "./credentials-oauth-BakY-0PA.mjs";
89
3
  export { PiOAuthCredentialProvider, apply, name, oauthCredentialRef };
90
-
91
- //# sourceMappingURL=credentials-oauth.mjs.map
package/dist/host.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-DsPaznXJ.mjs";
2
+ import { r as registerVisionCompanions, t as applyPiPackage } from "./runtime-DH0zEUpu.mjs";
3
3
  import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { createRequire } from "node:module";
5
5
  import { readFile } from "node:fs/promises";
@@ -105,7 +105,9 @@ async function applyPiHost(ctx, config, anchor) {
105
105
  }
106
106
  for (const failure of errors) {
107
107
  const log = ctx.logger;
108
- (log?.warn?.bind(log) ?? console.warn)(`[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`);
108
+ const message = `[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`;
109
+ log?.warn?.(message);
110
+ console.warn(message);
109
111
  }
110
112
  if (errors.length > 0 && errors.length === normalizeSpecs(config).length) throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0].name}: ${errors[0].error}`);
111
113
  }
package/dist/host.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"host.mjs","names":[],"sources":["../src/host.ts"],"sourcesContent":["// PiHostOnDSH: one DSH plugin that hosts unmodified Pi packages.\n//\n// Instead of converting each package into a vendored bundle, the host bundle\n// declares Pi packages as ordinary npm dependencies; DSH's plugin manager\n// (pnpm) installs them, and at load time this module resolves each installed\n// package, discovers its Pi entry points, and mounts it through the same\n// package-agnostic runtime as converted bundles. One host, any package —\n// there is deliberately no per-package branching here.\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { readdir, readFile, stat, writeFile, mkdir, cp } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPiPackage, registerVisionCompanions } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\nimport type { GeneratedRuntimeManifest, ResolvedPiPackage } from './types.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiHostPackageSpec {\n /** npm package name as installed in the host bundle's node_modules. */\n name: string\n /** Optional per-package config forwarded to the runtime. */\n config?: UnknownRecord\n}\n\nexport interface PiHostConfig {\n packages: Array<string | PiHostPackageSpec>\n /** Image-admission companions: default automatic; `false` off; explicit map narrows. */\n visionCompanions?: false | Record<string, readonly string[]>\n}\n\nfunction parseFrontmatter(text: string): { attributes: Record<string, string>; body: string } {\n const normalized = text.replace(/\\r\\n?/gu, '\\n')\n if (!normalized.startsWith('---')) return { attributes: {}, body: normalized }\n const endIndex = normalized.indexOf('\\n---', 3)\n if (endIndex === -1) return { attributes: {}, body: normalized }\n const attributes: Record<string, string> = {}\n for (const line of normalized.slice(4, endIndex).split('\\n')) {\n const separator = line.indexOf(':')\n if (separator === -1) continue\n const key = line.slice(0, separator).trim()\n let value = line.slice(separator + 1).trim()\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1)\n }\n if (key.length > 0) attributes[key] = value\n }\n return { attributes, body: normalized.slice(endIndex + 4).trim() }\n}\n\n/** Build a runtime manifest in place over an installed Pi package directory. */\nexport async function manifestForInstalled(pkg: ResolvedPiPackage): Promise<GeneratedRuntimeManifest> {\n const relativeTo = (file: string): string => relative(pkg.rootDir, file).replaceAll('\\\\', '/')\n\n const skillDirs = new Set<string>()\n for (const file of pkg.resources.skills) {\n // <dir>/<name>/SKILL.md contributes <dir>; a flat <dir>/<name>.md contributes <dir>.\n skillDirs.add(relativeTo(basename(file) === 'SKILL.md' ? dirname(dirname(file)) : dirname(file)))\n }\n\n const prompts: GeneratedRuntimeManifest['prompts'] = []\n const promptNames = new Set<string>()\n for (const source of pkg.resources.prompts) {\n const name = basename(source, '.md').toLowerCase().replace(/[^a-z0-9_-]+/gu, '-')\n if (promptNames.has(name)) throw new Error(`prompt command name collision in ${pkg.identity.name}: ${name}`)\n promptNames.add(name)\n const { attributes, body } = parseFrontmatter(await readFile(source, 'utf8'))\n const firstLine = body.split(/\\r?\\n/u).map(line => line.trim()).find(Boolean)\n prompts.push({\n name,\n description: attributes.description ?? firstLine ?? `Run migrated Pi prompt ${name}`,\n ...(attributes['argument-hint'] !== undefined ? { argumentHint: attributes['argument-hint'] } : {}),\n path: relativeTo(source),\n })\n }\n\n return {\n schemaVersion: 1,\n package: pkg.identity,\n extensions: pkg.resources.extensions.map(relativeTo),\n skillDirs: [...skillDirs].sort(),\n prompts,\n }\n}\n\nfunction normalizeSpecs(config: PiHostConfig): PiHostPackageSpec[] {\n const packages = Array.isArray(config?.packages) ? config.packages : []\n return packages.map(spec => (typeof spec === 'string' ? { name: spec } : spec))\n .filter(spec => typeof spec?.name === 'string' && spec.name.length > 0)\n}\n\nfunction resolveInstalledDir(anchor: string, packageName: string): string {\n const require = createRequire(anchor)\n try {\n return dirname(require.resolve(`${packageName}/package.json`))\n } catch {\n // Modern strict `exports` maps refuse the package.json subpath, and pure\n // ESM packages (no \"require\" condition) refuse CJS entry resolution too.\n // Locate the installed directory on the filesystem instead: probe every\n // node_modules candidate on the resolution path — no exports involved.\n for (const candidate of require.resolve.paths(packageName) ?? []) {\n const dir = join(candidate, packageName)\n if (existsSync(join(dir, 'package.json'))) return dir\n }\n throw new Error(`cannot locate the installed package directory for ${JSON.stringify(packageName)} near ${JSON.stringify(anchor)}`)\n }\n}\n\n/**\n * Mount every configured Pi package from the host bundle's own node_modules.\n * Packages that fail to mount report their error and do not take down the\n * host or their siblings — matching Pi's own per-extension error isolation.\n */\nexport async function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string): Promise<void> {\n const anchorPath = anchor ?? fileURLToPath(import.meta.url)\n registerVisionCompanions(ctx, config.visionCompanions)\n const errors: Array<{ name: string; error: string }> = []\n for (const spec of normalizeSpecs(config)) {\n try {\n const dir = resolveInstalledDir(anchorPath, spec.name)\n const pkg = await resolvePiPackage(dir)\n try {\n const manifest = await manifestForInstalled(pkg)\n await applyPiPackage(ctx, {\n rootUrl: pathToFileURL(`${pkg.rootDir}/`),\n manifest,\n ...(spec.config === undefined ? {} : { config: spec.config }),\n })\n } finally {\n await pkg.dispose()\n }\n } catch (error) {\n errors.push({ name: spec.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n const log = (ctx as unknown as { logger?: { warn?(message: string): void } }).logger\n const warn = log?.warn?.bind(log) ?? console.warn\n warn(`[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`)\n }\n if (errors.length > 0 && errors.length === normalizeSpecs(config).length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"host.mjs","names":[],"sources":["../src/host.ts"],"sourcesContent":["// PiHostOnDSH: one DSH plugin that hosts unmodified Pi packages.\n//\n// Instead of converting each package into a vendored bundle, the host bundle\n// declares Pi packages as ordinary npm dependencies; DSH's plugin manager\n// (pnpm) installs them, and at load time this module resolves each installed\n// package, discovers its Pi entry points, and mounts it through the same\n// package-agnostic runtime as converted bundles. One host, any package —\n// there is deliberately no per-package branching here.\n\nimport { existsSync, readFileSync } from 'node:fs'\nimport { readdir, readFile, stat, writeFile, mkdir, cp } from 'node:fs/promises'\nimport { createRequire } from 'node:module'\nimport { basename, dirname, join, relative } from 'node:path'\nimport { fileURLToPath, pathToFileURL } from 'node:url'\nimport type { Context } from '@deepseek-ai/cordis'\nimport { applyPiPackage, registerVisionCompanions } from './runtime.js'\nimport { resolvePiPackage } from './source.js'\nimport type { GeneratedRuntimeManifest, ResolvedPiPackage } from './types.js'\n\ntype UnknownRecord = Record<string, unknown>\n\nexport interface PiHostPackageSpec {\n /** npm package name as installed in the host bundle's node_modules. */\n name: string\n /** Optional per-package config forwarded to the runtime. */\n config?: UnknownRecord\n}\n\nexport interface PiHostConfig {\n packages: Array<string | PiHostPackageSpec>\n /** Image-admission companions: default automatic; `false` off; explicit map narrows. */\n visionCompanions?: false | Record<string, readonly string[]>\n}\n\nfunction parseFrontmatter(text: string): { attributes: Record<string, string>; body: string } {\n const normalized = text.replace(/\\r\\n?/gu, '\\n')\n if (!normalized.startsWith('---')) return { attributes: {}, body: normalized }\n const endIndex = normalized.indexOf('\\n---', 3)\n if (endIndex === -1) return { attributes: {}, body: normalized }\n const attributes: Record<string, string> = {}\n for (const line of normalized.slice(4, endIndex).split('\\n')) {\n const separator = line.indexOf(':')\n if (separator === -1) continue\n const key = line.slice(0, separator).trim()\n let value = line.slice(separator + 1).trim()\n if ((value.startsWith('\"') && value.endsWith('\"')) || (value.startsWith(\"'\") && value.endsWith(\"'\"))) {\n value = value.slice(1, -1)\n }\n if (key.length > 0) attributes[key] = value\n }\n return { attributes, body: normalized.slice(endIndex + 4).trim() }\n}\n\n/** Build a runtime manifest in place over an installed Pi package directory. */\nexport async function manifestForInstalled(pkg: ResolvedPiPackage): Promise<GeneratedRuntimeManifest> {\n const relativeTo = (file: string): string => relative(pkg.rootDir, file).replaceAll('\\\\', '/')\n\n const skillDirs = new Set<string>()\n for (const file of pkg.resources.skills) {\n // <dir>/<name>/SKILL.md contributes <dir>; a flat <dir>/<name>.md contributes <dir>.\n skillDirs.add(relativeTo(basename(file) === 'SKILL.md' ? dirname(dirname(file)) : dirname(file)))\n }\n\n const prompts: GeneratedRuntimeManifest['prompts'] = []\n const promptNames = new Set<string>()\n for (const source of pkg.resources.prompts) {\n const name = basename(source, '.md').toLowerCase().replace(/[^a-z0-9_-]+/gu, '-')\n if (promptNames.has(name)) throw new Error(`prompt command name collision in ${pkg.identity.name}: ${name}`)\n promptNames.add(name)\n const { attributes, body } = parseFrontmatter(await readFile(source, 'utf8'))\n const firstLine = body.split(/\\r?\\n/u).map(line => line.trim()).find(Boolean)\n prompts.push({\n name,\n description: attributes.description ?? firstLine ?? `Run migrated Pi prompt ${name}`,\n ...(attributes['argument-hint'] !== undefined ? { argumentHint: attributes['argument-hint'] } : {}),\n path: relativeTo(source),\n })\n }\n\n return {\n schemaVersion: 1,\n package: pkg.identity,\n extensions: pkg.resources.extensions.map(relativeTo),\n skillDirs: [...skillDirs].sort(),\n prompts,\n }\n}\n\nfunction normalizeSpecs(config: PiHostConfig): PiHostPackageSpec[] {\n const packages = Array.isArray(config?.packages) ? config.packages : []\n return packages.map(spec => (typeof spec === 'string' ? { name: spec } : spec))\n .filter(spec => typeof spec?.name === 'string' && spec.name.length > 0)\n}\n\nfunction resolveInstalledDir(anchor: string, packageName: string): string {\n const require = createRequire(anchor)\n try {\n return dirname(require.resolve(`${packageName}/package.json`))\n } catch {\n // Modern strict `exports` maps refuse the package.json subpath, and pure\n // ESM packages (no \"require\" condition) refuse CJS entry resolution too.\n // Locate the installed directory on the filesystem instead: probe every\n // node_modules candidate on the resolution path — no exports involved.\n for (const candidate of require.resolve.paths(packageName) ?? []) {\n const dir = join(candidate, packageName)\n if (existsSync(join(dir, 'package.json'))) return dir\n }\n throw new Error(`cannot locate the installed package directory for ${JSON.stringify(packageName)} near ${JSON.stringify(anchor)}`)\n }\n}\n\n/**\n * Mount every configured Pi package from the host bundle's own node_modules.\n * Packages that fail to mount report their error and do not take down the\n * host or their siblings — matching Pi's own per-extension error isolation.\n */\nexport async function applyPiHost(ctx: Context, config: PiHostConfig, anchor?: string): Promise<void> {\n const anchorPath = anchor ?? fileURLToPath(import.meta.url)\n registerVisionCompanions(ctx, config.visionCompanions)\n const errors: Array<{ name: string; error: string }> = []\n for (const spec of normalizeSpecs(config)) {\n try {\n const dir = resolveInstalledDir(anchorPath, spec.name)\n const pkg = await resolvePiPackage(dir)\n try {\n const manifest = await manifestForInstalled(pkg)\n await applyPiPackage(ctx, {\n rootUrl: pathToFileURL(`${pkg.rootDir}/`),\n manifest,\n ...(spec.config === undefined ? {} : { config: spec.config }),\n })\n } finally {\n await pkg.dispose()\n }\n } catch (error) {\n errors.push({ name: spec.name, error: error instanceof Error ? error.message : String(error) })\n }\n }\n for (const failure of errors) {\n // Console AND logger, the same rule the engine states for its own mount\n // line: a profile's logger level must never be able to hide which packages\n // did not mount. It could, and it did — a package failed to mount and the\n // only symptom was its absence from a list nobody was diffing.\n const log = (ctx as unknown as { logger?: { warn?(message: string): void } }).logger\n const message = `[pi2dsh host] failed to mount ${failure.name}: ${failure.error}`\n log?.warn?.(message)\n console.warn(message)\n }\n if (errors.length > 0 && errors.length === normalizeSpecs(config).length) {\n throw new Error(`pi2dsh host mounted no packages; first failure: ${errors[0]!.name}: ${errors[0]!.error}`)\n }\n}\n"],"mappings":"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"}
package/dist/index.d.mts CHANGED
@@ -42,15 +42,27 @@ declare function apply(ctx: Context, config?: EngineConfig): Promise<void>;
42
42
  interface Rule {
43
43
  level: CompatibilityLevel;
44
44
  detail: string;
45
+ /** How the mapping is built — the DSH seam or service that carries it. */
46
+ design?: string;
47
+ }
48
+ /**
49
+ * A Pi extension surface. `design` is REQUIRED here and optional on Rule: an
50
+ * imported symbol is served by a shim family the docs describe once, but every
51
+ * surface a package can call has to name the DSH mechanism behind it, or the
52
+ * documentation degrades into a list of verdicts nobody can act on. Adding a
53
+ * surface without one is a type error, which is the point.
54
+ */
55
+ interface SurfaceRule extends Rule {
56
+ design: string;
45
57
  }
46
58
  declare const PI_CODING_AGENT_PACKAGES: readonly ["@earendil-works/pi-coding-agent", "@mariozechner/pi-coding-agent"];
47
59
  declare const PI_TUI_PACKAGES: readonly ["@earendil-works/pi-tui", "@mariozechner/pi-tui"];
48
60
  declare const PI_AI_PACKAGES: readonly ["@earendil-works/pi-ai", "@mariozechner/pi-ai"];
49
61
  declare const HOST_IMPORT_RULES: Readonly<Record<string, Readonly<Record<string, Rule>>>>;
50
- declare const CONTEXT_RULES: Readonly<Record<string, Rule>>;
51
- declare const UI_CONTEXT_RULES: Readonly<Record<string, Rule>>;
52
- declare const API_RULES: Readonly<Record<string, Rule>>;
53
- declare const EVENT_RULES: Readonly<Record<string, Rule>>;
62
+ declare const CONTEXT_RULES: Readonly<Record<string, SurfaceRule>>;
63
+ declare const UI_CONTEXT_RULES: Readonly<Record<string, SurfaceRule>>;
64
+ declare const API_RULES: Readonly<Record<string, SurfaceRule>>;
65
+ declare const EVENT_RULES: Readonly<Record<string, SurfaceRule>>;
54
66
  declare function ruleForApi(method: string): Rule | undefined;
55
67
  declare function ruleForEvent(event: string): Rule;
56
68
  declare function ruleForHostImport(packageName: string, importedName: string): Rule | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/engine.ts","../src/compatibility.ts","../src/mcp-config.ts","../src/source.ts","../src/index.ts"],"mappings":";;;;;UA6BiB;;EAEf;;EAEA;;;;;;;;EAQA,2BAA2B;;;iBAIb,gBAAgB;UAUtB;EACR;EACA;;;;;;;;iBAgBoB,0BACpB,qBACA;EAAW;EAAoB,QAAQ;IACtC,QAAQ;;cA4CE;cACA;iBAES,MAAM,KAAK,SAAS,SAAQ,eAAoB;;;UCzH5D;EACR,OAAO;EACP;;cAKW;cAKA;cAKA;cAWA,mBAAmB,SAAS,eAAe,SAAS,eAAe;cAmNnE,eAAe,SAAS,eAAe;cA4BvC,kBAAkB,SAAS,eAAe;cA+B1C,WAAW,SAAS,eAAe;cAkHnC,aAAa,SAAS,eAAe;iBA6FlC,WAAW,iBAAiB;iBAI5B,aAAa,gBAAgB;iBAO7B,kBAAkB,qBAAqB,uBAAuB;iBAS9D,uBAAuB,mBAAmB;iBAI1C,yBAAyB,mBAAmB;;;KCtgBvD,gBAAgB;UAEJ;EACf;EACA;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA,UAAU;EACV;;UAGe;EACf;EACA;;UAGe;EACf,SAAS;EACT,SAAS;EACT,UAAU;EACV;;iBA6Cc,oBAAoB,aAAa;EAA8B,SAAS,YAAY;EAAc;;iBAoElG,mBAAmB,aAAa,wBAA4B;;iBAsD5D,eAAe,QAAQ;;;iBC5EjB,iBAAiB,gBAAgB,eAAsB,QAAQ;;;;;;;;iBChH/D,eAAe,KAAK,oBAAoB,QAAQ"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/engine.ts","../src/compatibility.ts","../src/mcp-config.ts","../src/source.ts","../src/index.ts"],"mappings":";;;;;UA6BiB;;EAEf;;EAEA;;;;;;;;EAQA,2BAA2B;;;iBAIb,gBAAgB;UAUtB;EACR;EACA;;;;;;;;iBAgBoB,0BACpB,qBACA;EAAW;EAAoB,QAAQ;IACtC,QAAQ;;cA4CE;cACA;iBAES,MAAM,KAAK,SAAS,SAAQ,eAAoB;;;UCzH5D;EACR,OAAO;EACP;;EAEA;;;;;;;;;UAUQ,oBAAoB;EAC5B;;cAQW;cAKA;cAKA;cAWA,mBAAmB,SAAS,eAAe,SAAS,eAAe;cAmNnE,eAAe,SAAS,eAAe;cA4BvC,kBAAkB,SAAS,eAAe;cA+B1C,WAAW,SAAS,eAAe;cA6InC,aAAa,SAAS,eAAe;iBAgHlC,WAAW,iBAAiB;iBAI5B,aAAa,gBAAgB;iBAO7B,kBAAkB,qBAAqB,uBAAuB;iBAS9D,uBAAuB,mBAAmB;iBAI1C,yBAAyB,mBAAmB;;;KCpkBvD,gBAAgB;UAEJ;EACf;EACA;EACA;EACA;EACA;EACA,MAAM;EACN;EACA;EACA,UAAU;EACV;;UAGe;EACf;EACA;;UAGe;EACf,SAAS;EACT,SAAS;EACT,UAAU;EACV;;iBA6Cc,oBAAoB,aAAa;EAA8B,SAAS,YAAY;EAAc;;iBAoElG,mBAAmB,aAAa,wBAA4B;;iBAsD5D,eAAe,QAAQ;;;iBC5EjB,iBAAiB,gBAAgB,eAAsB,QAAQ;;;;;;;;iBChH/D,eAAe,KAAK,oBAAoB,QAAQ"}
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
 
2
- import { r as registerVisionCompanions } from "./runtime-DsPaznXJ.mjs";
2
+ import { r as registerVisionCompanions } from "./runtime-DH0zEUpu.mjs";
3
3
  import { t as resolvePiPackage } from "./source-0sA5z08z.mjs";
4
4
  import { applyPiHost, manifestForInstalled } from "./host.mjs";
5
- import { a as CONTEXT_RULES, c as PI_AI_PACKAGES, d as UI_CONTEXT_RULES, f as ruleForApi, g as ruleForUiContextProperty, h as ruleForHostImport, i as API_RULES, l as PI_CODING_AGENT_PACKAGES, m as ruleForEvent, n as convertPiMcpConfig, o as EVENT_RULES, p as ruleForContextProperty, r as renderMcpPatch, s as HOST_IMPORT_RULES, t as collectPiMcpServers, u as PI_TUI_PACKAGES } from "./mcp-config-DHkfjdvX.mjs";
5
+ import { a as CONTEXT_RULES, c as PI_AI_PACKAGES, d as UI_CONTEXT_RULES, f as ruleForApi, g as ruleForUiContextProperty, h as ruleForHostImport, i as API_RULES, l as PI_CODING_AGENT_PACKAGES, m as ruleForEvent, n as convertPiMcpConfig, o as EVENT_RULES, p as ruleForContextProperty, r as renderMcpPatch, s as HOST_IMPORT_RULES, t as collectPiMcpServers, u as PI_TUI_PACKAGES } from "./mcp-config-CZepacin.mjs";
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { existsSync } from "node:fs";
8
8
  import { dirname, join } from "node:path";
@@ -114,7 +114,7 @@ async function apply(ctx, config = {}) {
114
114
  * @returns the compatibility report.
115
115
  */
116
116
  async function analyzePackage(pkg) {
117
- const { analyzePackage: run } = await import("./analyzer-CEjVQKXA.mjs");
117
+ const { analyzePackage: run } = await import("./analyzer-CXNbeBAe.mjs");
118
118
  return run(pkg);
119
119
  }
120
120
  //#endregion