@rexvane/piweb 0.3.15 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +4 -4
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/prerender-manifest.json +3 -3
  5. package/.next/react-loadable-manifest.json +3 -3
  6. package/.next/required-server-files.js +1 -3
  7. package/.next/required-server-files.json +1 -3
  8. package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/server/app/_global-error.html +1 -1
  10. package/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  14. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  15. package/.next/server/app/_not-found.html +1 -1
  16. package/.next/server/app/_not-found.rsc +2 -2
  17. package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  18. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  19. package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  20. package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
  21. package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
  22. package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
  23. package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
  24. package/.next/server/app/api/growth/route.js +1 -1
  25. package/.next/server/app/api/growth/route.js.nft.json +1 -1
  26. package/.next/server/app/api/models/route.js +1 -1
  27. package/.next/server/app/api/models/route.js.nft.json +1 -1
  28. package/.next/server/app/api/pi-settings/route.js +1 -1
  29. package/.next/server/app/api/plugins/route.js.nft.json +1 -1
  30. package/.next/server/app/api/prompts/route.js.nft.json +1 -1
  31. package/.next/server/app/api/security/route.js.nft.json +1 -1
  32. package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
  33. package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
  34. package/.next/server/app/api/sessions/import/route.js +3 -3
  35. package/.next/server/app/api/sessions/route.js.nft.json +1 -1
  36. package/.next/server/app/api/skills/route.js.nft.json +1 -1
  37. package/.next/server/app/api/update/route.js +1 -1
  38. package/.next/server/app/api/update/route.js.nft.json +1 -1
  39. package/.next/server/app/api/version/route.js +1 -1
  40. package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
  41. package/.next/server/app/index.html +1 -1
  42. package/.next/server/app/index.rsc +4 -4
  43. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  44. package/.next/server/app/index.segments/_full.segment.rsc +4 -4
  45. package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  46. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  47. package/.next/server/app/login.html +1 -1
  48. package/.next/server/app/login.rsc +3 -3
  49. package/.next/server/app/login.segments/_full.segment.rsc +3 -3
  50. package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
  51. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +3 -3
  52. package/.next/server/app/page.js +1 -1
  53. package/.next/server/app/page_client-reference-manifest.js +1 -1
  54. package/.next/server/app-paths-manifest.json +4 -4
  55. package/.next/server/chunks/496.js +1 -1
  56. package/.next/server/chunks/686.js +4 -4
  57. package/.next/server/middleware-build-manifest.js +1 -1
  58. package/.next/server/middleware-react-loadable-manifest.js +1 -1
  59. package/.next/server/pages/404.html +1 -1
  60. package/.next/server/pages/500.html +1 -1
  61. package/.next/server/server-reference-manifest.json +1 -1
  62. package/.next/static/chunks/2716.93be8ad5c970c516.js +1 -0
  63. package/.next/static/chunks/4632.112d2e893d48d188.js +1 -0
  64. package/.next/static/chunks/4673.c1fb1580efc6ace7.js +1 -0
  65. package/.next/static/chunks/4686-f4a037d609cea3f2.js +1 -0
  66. package/.next/static/chunks/app/page-b51087831582ee37.js +1 -0
  67. package/.next/static/chunks/{webpack-42c5aae79b0e5d8c.js → webpack-c31263fd83d75ce6.js} +1 -1
  68. package/.next/static/css/850c2fd130ffa2fc.css +1 -0
  69. package/README.md +17 -19
  70. package/assets/archive.jpg +0 -0
  71. package/assets/growth-1.jpg +0 -0
  72. package/assets/growth-2.jpg +0 -0
  73. package/assets/import.jpg +0 -0
  74. package/assets/main.jpg +0 -0
  75. package/assets/outline.jpg +0 -0
  76. package/assets/prompts-1.jpg +0 -0
  77. package/assets/prompts-2.jpg +0 -0
  78. package/assets/providers-1.jpg +0 -0
  79. package/assets/providers-2.jpg +0 -0
  80. package/assets/providers-3.jpg +0 -0
  81. package/assets/trace-1.jpg +0 -0
  82. package/assets/trace-2.png +0 -0
  83. package/assets/trace-3.png +0 -0
  84. package/assets/trace-4.png +0 -0
  85. package/assets/viewer-1.jpg +0 -0
  86. package/assets/viewer-2.jpg +0 -0
  87. package/bin/pi.js +50 -0
  88. package/next.config.ts +32 -7
  89. package/package.json +2 -6
  90. package/scripts/launcher.mjs +1 -1
  91. package/scripts/verify-package.mjs +0 -53
  92. package/src/app/api/growth/route.ts +14 -14
  93. package/src/app/api/models/route.ts +2 -1
  94. package/src/app/globals.css +90 -0
  95. package/src/components/AppShell.tsx +30 -16
  96. package/src/components/ChatWindow.tsx +192 -6
  97. package/src/components/FileViewer.tsx +10 -9
  98. package/src/components/ProjectPanel.tsx +111 -94
  99. package/src/components/SessionImportSection.tsx +20 -36
  100. package/src/hooks/useGrowth.ts +123 -156
  101. package/src/hooks/usePiWeb.ts +7 -10
  102. package/src/i18n.tsx +39 -39
  103. package/src/lib/agent-manager.ts +10 -8
  104. package/src/lib/growth-service.ts +390 -166
  105. package/src/lib/growth-tracker.ts +64 -264
  106. package/src/lib/growth-turns.ts +31 -1
  107. package/src/lib/models/model-scope.ts +15 -0
  108. package/src/lib/models-service.ts +16 -0
  109. package/src/lib/pi-settings.ts +7 -0
  110. package/src/lib/session-import/claude.ts +2 -13
  111. package/src/lib/session-import/codex.ts +8 -44
  112. package/src/lib/session-import/files.ts +0 -14
  113. package/src/lib/session-import/grok.ts +24 -54
  114. package/src/lib/session-import/types.ts +0 -12
  115. package/src/lib/types.ts +27 -22
  116. package/tsconfig.json +2 -1
  117. package/.next/static/chunks/2716.a9e808190a4e2424.js +0 -1
  118. package/.next/static/chunks/4632.0ed92ae42905ce40.js +0 -1
  119. package/.next/static/chunks/4673.d3d0a7f2599080fb.js +0 -1
  120. package/.next/static/chunks/4686-7d694def920ff093.js +0 -1
  121. package/.next/static/chunks/app/page-9a5440047c5a415c.js +0 -1
  122. package/.next/static/css/b2d35abc828d4713.css +0 -1
  123. package/assets/archive.png +0 -0
  124. package/assets/growth-1.png +0 -0
  125. package/assets/growth-2.png +0 -0
  126. package/assets/import.png +0 -0
  127. package/assets/main.png +0 -0
  128. package/assets/outline.png +0 -0
  129. package/assets/prompts-1.png +0 -0
  130. package/assets/prompts-2.png +0 -0
  131. package/assets/providers-1.png +0 -0
  132. package/assets/providers-2.png +0 -0
  133. package/assets/providers-3.png +0 -0
  134. package/assets/trace-1.png +0 -0
  135. package/assets/viewer-1.png +0 -0
  136. package/assets/viewer-2.png +0 -0
  137. package/scripts/managed-niubash-assets.mjs +0 -146
  138. package/scripts/prepare-niubash-packages.mjs +0 -182
  139. package/scripts/smoke-managed-niubash.mjs +0 -62
  140. package/src/lib/growth-rounds.ts +0 -84
  141. package/src/lib/managed-niubash.ts +0 -227
  142. /package/.next/static/{DdBwPYoHBKtaJ97QIr6Co → zsynuO_9Kx6nrXgkTUJ04}/_buildManifest.js +0 -0
  143. /package/.next/static/{DdBwPYoHBKtaJ97QIr6Co → zsynuO_9Kx6nrXgkTUJ04}/_ssgManifest.js +0 -0
@@ -1,84 +0,0 @@
1
- import type { GrowthStep } from "./types";
2
-
3
- export interface GrowthRound {
4
- /** 真实用户轮次;不会因没有文件改动而跳号。 */
5
- id: number;
6
- startedAt: number;
7
- first: GrowthStep | null;
8
- /** 此轮结束时可用的最后一张快照;无改动轮沿用前一张。 */
9
- last: GrowthStep | null;
10
- steps: GrowthStep[];
11
- /** 相邻两轮终点的 tree;第一轮从 prompt 前的基线开始。 */
12
- fromTree: string | null;
13
- toTree: string | null;
14
- /** 旧会话可能在启用生长账本前已发生,不能假装它是零改动。 */
15
- recorded: boolean;
16
- }
17
-
18
- /** 兼容没有消息时间戳的调用者:按账本里的 turn 标记分组。 */
19
- function ledgerRounds(steps: GrowthStep[]): GrowthRound[] {
20
- const rounds: GrowthRound[] = [];
21
- let current: GrowthStep[] = [];
22
- const flush = () => {
23
- if (!current.length) return;
24
- const first = current[0];
25
- const last = current[current.length - 1];
26
- rounds.push({ id: rounds.length + 1, startedAt: first.ts, first, last, steps: current, fromTree: first.parent, toTree: last.tree, recorded: true });
27
- current = [];
28
- };
29
- for (const [index, step] of steps.entries()) {
30
- if (index === 0 && step.initial) continue;
31
- if (step.kind === "baseline") {
32
- flush();
33
- continue;
34
- }
35
- if (step.kind === "external" || step.kind === "manual") flush();
36
- current.push(step);
37
- if (step.kind === "turn" || step.kind === "external" || step.kind === "manual") flush();
38
- }
39
- flush();
40
- return rounds;
41
- }
42
-
43
- /** 轮数以持久会话的用户消息为准;快照只负责证明这一轮的前后树与差异。 */
44
- export function buildGrowthRounds(steps: GrowthStep[], turnStarts?: number[]): GrowthRound[] {
45
- if (turnStarts === undefined) return ledgerRounds(steps);
46
- if (!turnStarts.length) return [];
47
- const starts: number[] = [];
48
- for (const value of turnStarts) {
49
- const previous = starts[starts.length - 1];
50
- starts.push(Number.isFinite(value) ? Math.max(value, previous === undefined ? value : previous + 0.001) : (previous ?? 0) + 0.001);
51
- }
52
- const ordered = [...steps].sort((a, b) => a.ts - b.ts || a.seq - b.seq);
53
- const rounds: GrowthRound[] = [];
54
- let cursor = 0;
55
- let before: GrowthStep | null = null;
56
- let previousEnd: GrowthStep | null = null;
57
- for (let index = 0; index < starts.length; index += 1) {
58
- const start = starts[index];
59
- const end = starts[index + 1] ?? Infinity;
60
- while (cursor < ordered.length && ordered[cursor].ts < start) before = ordered[cursor++];
61
- const atStart = before;
62
- const current: GrowthStep[] = [];
63
- while (cursor < ordered.length && ordered[cursor].ts < end) {
64
- const step = ordered[cursor++];
65
- if (step.kind !== "baseline") current.push(step);
66
- before = step;
67
- }
68
- const first = current[0] ?? null;
69
- const last: GrowthStep | null = current[current.length - 1] ?? (atStart && (!previousEnd || atStart.ts > previousEnd.ts) ? atStart : previousEnd);
70
- const fromTree = previousEnd?.tree ?? atStart?.tree ?? first?.parent ?? null;
71
- rounds.push({
72
- id: index + 1,
73
- startedAt: start,
74
- first,
75
- last,
76
- steps: current,
77
- fromTree,
78
- toTree: last?.tree ?? null,
79
- recorded: current.length > 0 && fromTree !== null && !first?.initial,
80
- });
81
- previousEnd = last;
82
- }
83
- return rounds;
84
- }
@@ -1,227 +0,0 @@
1
- import crypto from "node:crypto";
2
- import fs from "node:fs";
3
- import { createRequire } from "node:module";
4
- import path from "node:path";
5
- import { createBashToolDefinition, type ToolDefinition } from "@earendil-works/pi-coding-agent";
6
- import { APP_ROOT } from "./app-root";
7
-
8
- const DISTRIBUTION_VERSION = "1.1.4-piweb.0";
9
- const NIUBASH_VERSION = "1.1.4";
10
- const SOURCE_REPOSITORY = "https://github.com/unixwin/niubash";
11
- const SOURCE_TAG = "v1.1.4";
12
-
13
- const PACKAGE_BY_ARCH = {
14
- x64: {
15
- packageName: "@rexvane/piweb-niubash-win32-x64",
16
- directory: "piweb-niubash-win32-x64",
17
- asset: "niubash-v1.1.4-win-x64.zip",
18
- assetSha256: "9f543634288a316f7ed8e5a76ff794eca402cb21e74b8f8a551daffafde162f5",
19
- machine: 0x8664,
20
- },
21
- arm64: {
22
- packageName: "@rexvane/piweb-niubash-win32-arm64",
23
- directory: "piweb-niubash-win32-arm64",
24
- asset: "niubash-v1.1.4-win-arm64.zip",
25
- assetSha256: "77e98ba8d7f6be70c35388bcc28bdbc883c06bcaafffbe186a88788ad32ccf00",
26
- machine: 0xaa64,
27
- },
28
- } as const;
29
-
30
- const LICENSE_HASHES = {
31
- "NIUBASH.txt": "1290aae99e71239ee3154e7847830e49e7a36c758fa6d659d73fa752dc9f81c8",
32
- "RUBASH.txt": "e426a75a20633f3088da22aef61a5a44d06f1f884c600f6011d00702c3989079",
33
- "WINUXCMD.txt": "8ffc4ed56f5e6ab8183a7ece2e3aa6ea4800b0a4e8170d11bd60856f2dd760f3",
34
- } as const;
35
-
36
- export type SupportedArch = keyof typeof PACKAGE_BY_ARCH;
37
-
38
- type PackageExport = {
39
- schema?: unknown;
40
- packageName?: unknown;
41
- distributionVersion?: unknown;
42
- niubashVersion?: unknown;
43
- platform?: unknown;
44
- arch?: unknown;
45
- runtimeDir?: unknown;
46
- shellPath?: unknown;
47
- manifestPath?: unknown;
48
- };
49
-
50
- type LoadedPackage = { entryPath: string; value: unknown };
51
-
52
- /**
53
- * npm 安装(全局或本地)普通情况下由 Node 解析:npm 会把可选包提升到任意祖先
54
- * node_modules,所以解析必须从包自己的 package.json 出发。仓库检出(开发 / CI /
55
- * 发布前的 runtime 准备)没有安装这一步,此时按约定找 platform-packages/<dir>——
56
- * 这与平台包发布后的目录布局一一对应,不引入第二套解析规则。
57
- */
58
- export function resolveManagedNiubashPackageEntry(packageName: string, arch: SupportedArch, root = APP_ROOT): LoadedPackage {
59
- const require = createRequire(path.join(root, "package.json"));
60
- try {
61
- return { entryPath: require.resolve(packageName), value: require(packageName) };
62
- } catch (error) {
63
- const checkoutEntry = path.join(root, "platform-packages", PACKAGE_BY_ARCH[arch].directory, "index.cjs");
64
- if (fs.existsSync(checkoutEntry)) return { entryPath: checkoutEntry, value: require(checkoutEntry) };
65
- throw error;
66
- }
67
- }
68
-
69
- export type ManagedNiubashRuntime = {
70
- packageName: string;
71
- distributionVersion: string;
72
- niubashVersion: string;
73
- arch: SupportedArch;
74
- runtimeDir: string;
75
- shellPath: string;
76
- };
77
-
78
- export type ManagedNiubashResolveOptions = {
79
- platform?: string;
80
- arch?: string;
81
- /** Base for package resolution; tests and the prepare script point it at a checkout root. */
82
- root?: string;
83
- loadPackage?: (packageName: string, arch: SupportedArch) => LoadedPackage;
84
- warn?: (message: string) => void;
85
- };
86
-
87
- function sha256File(filename: string): string {
88
- return crypto.createHash("sha256").update(fs.readFileSync(filename)).digest("hex");
89
- }
90
-
91
- function readJson(filename: string): any {
92
- const stat = fs.statSync(filename);
93
- if (!stat.isFile() || stat.size > 2 * 1024 * 1024) throw new Error(`${path.basename(filename)} is missing or too large`);
94
- return JSON.parse(fs.readFileSync(filename, "utf8"));
95
- }
96
-
97
- function samePath(left: string, right: string): boolean {
98
- const a = path.resolve(left);
99
- const b = path.resolve(right);
100
- return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
101
- }
102
-
103
- function peMachine(filename: string): number {
104
- const data = fs.readFileSync(filename);
105
- if (data.length < 64 || data[0] !== 0x4d || data[1] !== 0x5a) throw new Error(`${path.basename(filename)} is not a PE executable`);
106
- const offset = data.readUInt32LE(0x3c);
107
- if (offset + 6 > data.length || data.toString("ascii", offset, offset + 4) !== "PE\0\0") {
108
- throw new Error(`${path.basename(filename)} has an invalid PE header`);
109
- }
110
- return data.readUInt16LE(offset + 4);
111
- }
112
-
113
- function validatePackage(loaded: LoadedPackage, arch: SupportedArch): ManagedNiubashRuntime {
114
- const expected = PACKAGE_BY_ARCH[arch];
115
- if (!loaded.value || typeof loaded.value !== "object") throw new Error(`${expected.packageName} has no runtime export`);
116
- const exported = loaded.value as PackageExport;
117
- const packageRoot = path.dirname(path.resolve(loaded.entryPath));
118
- const runtimeDir = path.join(packageRoot, "runtime");
119
- const shellPath = path.join(runtimeDir, "niu.exe");
120
- const manifestPath = path.join(packageRoot, "runtime-manifest.json");
121
-
122
- const checks: Array<[unknown, unknown, string]> = [
123
- [exported.schema, 1, "export schema"],
124
- [exported.packageName, expected.packageName, "export package name"],
125
- [exported.distributionVersion, DISTRIBUTION_VERSION, "export distribution version"],
126
- [exported.niubashVersion, NIUBASH_VERSION, "export niubash version"],
127
- [exported.platform, "win32", "export platform"],
128
- [exported.arch, arch, "export architecture"],
129
- ];
130
- for (const [actual, wanted, label] of checks) {
131
- if (actual !== wanted) throw new Error(`${label} mismatch`);
132
- }
133
- if (typeof exported.runtimeDir !== "string" || !samePath(exported.runtimeDir, runtimeDir)) throw new Error("runtime directory escapes its package");
134
- if (typeof exported.shellPath !== "string" || !samePath(exported.shellPath, shellPath)) throw new Error("shell path escapes its package");
135
- if (typeof exported.manifestPath !== "string" || !samePath(exported.manifestPath, manifestPath)) throw new Error("manifest path escapes its package");
136
-
137
- const packageInfo = readJson(path.join(packageRoot, "package.json"));
138
- if (packageInfo.name !== expected.packageName || packageInfo.version !== DISTRIBUTION_VERSION) throw new Error("package identity mismatch");
139
- if (packageInfo.os?.length !== 1 || packageInfo.os[0] !== "win32") throw new Error("package OS restriction mismatch");
140
- if (packageInfo.cpu?.length !== 1 || packageInfo.cpu[0] !== arch) throw new Error("package CPU restriction mismatch");
141
-
142
- const manifest = readJson(manifestPath);
143
- if (manifest.schema !== 1
144
- || manifest.source?.repository !== SOURCE_REPOSITORY
145
- || manifest.source?.tag !== SOURCE_TAG
146
- || manifest.source?.asset !== expected.asset
147
- || manifest.source?.sha256 !== expected.assetSha256) {
148
- throw new Error("runtime provenance mismatch");
149
- }
150
- if (!manifest.files || typeof manifest.files !== "object" || Array.isArray(manifest.files)) throw new Error("runtime file manifest is missing");
151
- const files = Object.entries(manifest.files) as Array<[string, unknown]>;
152
- if (files.length < 300 || files.length > 1000) throw new Error(`runtime file count is invalid (${files.length})`);
153
- for (const [relative, digest] of files) {
154
- if (!relative || relative.includes("\\") || path.posix.isAbsolute(relative) || /^[A-Za-z]:/.test(relative) || relative.split("/").includes("..")) {
155
- throw new Error(`unsafe runtime path: ${relative}`);
156
- }
157
- if (typeof digest !== "string" || !/^[a-f0-9]{64}$/.test(digest)) throw new Error(`invalid runtime hash: ${relative}`);
158
- const filename = path.join(runtimeDir, ...relative.split("/"));
159
- if (!fs.statSync(filename).isFile()) throw new Error(`runtime file is missing: ${relative}`);
160
- if (sha256File(filename) !== digest) throw new Error(`runtime file failed integrity check: ${relative}`);
161
- }
162
- for (const required of ["niu.exe", "winuxcmd/usr/bin/winuxcmd.exe", "bundles/oh-my-niu/bundle.toml"]) {
163
- if (!(required in manifest.files)) throw new Error(`runtime file is missing: ${required}`);
164
- }
165
- for (const executable of [shellPath, path.join(runtimeDir, "winuxcmd", "usr", "bin", "winuxcmd.exe")]) {
166
- if (peMachine(executable) !== expected.machine) throw new Error(`${path.basename(executable)} architecture mismatch`);
167
- }
168
- for (const [name, digest] of Object.entries(LICENSE_HASHES)) {
169
- if (sha256File(path.join(packageRoot, "licenses", name)) !== digest) throw new Error(`license integrity check failed: ${name}`);
170
- }
171
-
172
- return Object.freeze({
173
- packageName: expected.packageName,
174
- distributionVersion: DISTRIBUTION_VERSION,
175
- niubashVersion: NIUBASH_VERSION,
176
- arch,
177
- runtimeDir,
178
- shellPath,
179
- });
180
- }
181
-
182
- function reasonOf(error: unknown, packageName: string): string {
183
- if ((error as NodeJS.ErrnoException | undefined)?.code === "MODULE_NOT_FOUND") return `${packageName} is not installed`;
184
- return error instanceof Error ? error.message : String(error);
185
- }
186
-
187
- /** Resolve and fully validate the platform package. Failures deliberately fall back to Pi's normal Bash. */
188
- export function resolveManagedNiubash(options: ManagedNiubashResolveOptions = {}): ManagedNiubashRuntime | null {
189
- const platform = options.platform ?? process.platform;
190
- const arch = options.arch ?? process.arch;
191
- if (platform !== "win32") return null;
192
- const expected = PACKAGE_BY_ARCH[arch as SupportedArch];
193
- if (!expected) {
194
- (options.warn ?? console.warn)(`[piweb] Managed niubash does not support Windows ${arch}; using Pi's configured/default Bash.`);
195
- return null;
196
- }
197
- const supported = arch as SupportedArch;
198
- try {
199
- const loaded = options.loadPackage
200
- ? options.loadPackage(expected.packageName, supported)
201
- : resolveManagedNiubashPackageEntry(expected.packageName, supported, options.root);
202
- return validatePackage(loaded, supported);
203
- } catch (error) {
204
- (options.warn ?? console.warn)(
205
- `[piweb] Managed niubash is unavailable (${reasonOf(error, expected.packageName)}); using Pi's configured/default Bash. `
206
- + "Reinstall @rexvane/piweb to restore the bundled Windows shell (`npm run prepare:niubash` in a checkout).",
207
- );
208
- return null;
209
- }
210
- }
211
-
212
- const globalForManagedNiubash = globalThis as typeof globalThis & {
213
- __piWebManagedNiubash?: { resolved: ManagedNiubashRuntime | null };
214
- };
215
-
216
- function defaultRuntime(): ManagedNiubashRuntime | null {
217
- if (!globalForManagedNiubash.__piWebManagedNiubash) {
218
- globalForManagedNiubash.__piWebManagedNiubash = { resolved: resolveManagedNiubash() };
219
- }
220
- return globalForManagedNiubash.__piWebManagedNiubash.resolved;
221
- }
222
-
223
- /** Same-name custom tool intentionally replaces Pi's built-in bash only inside PiWeb sessions. */
224
- export function createManagedNiubashTools(cwd: string, options?: ManagedNiubashResolveOptions): ToolDefinition[] {
225
- const runtime = options ? resolveManagedNiubash(options) : defaultRuntime();
226
- return runtime ? [createBashToolDefinition(cwd, { shellPath: runtime.shellPath }) as ToolDefinition] : [];
227
- }