@chainingintention/pi-web-cn 1.202606.3

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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +364 -0
  3. package/dist/cli.js +960 -0
  4. package/dist/cli.js.map +1 -0
  5. package/dist/client/apple-touch-icon.png +0 -0
  6. package/dist/client/assets/CodeViewer-B4nxYc0g.js +4 -0
  7. package/dist/client/assets/TerminalPanel-htr2dU1I.js +122 -0
  8. package/dist/client/assets/index-BjUH4a8R.js +1994 -0
  9. package/dist/client/assets/vendor-editor-core-B4Sq6exx.js +12 -0
  10. package/dist/client/assets/vendor-editor-languages-DznYbTkJ.js +26 -0
  11. package/dist/client/assets/vendor-editor-legacy-B4QLsWF8.js +1 -0
  12. package/dist/client/assets/vendor-terminal-DDGTF8rc.css +1 -0
  13. package/dist/client/assets/vendor-terminal-DjQ08hXu.js +16 -0
  14. package/dist/client/favicon.svg +11 -0
  15. package/dist/client/index.html +60 -0
  16. package/dist/client/manifest.webmanifest +24 -0
  17. package/dist/client/pwa-icon-192.png +0 -0
  18. package/dist/client/pwa-icon-512.png +0 -0
  19. package/dist/config.js +154 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/pi-web-plugins/info/package.json +9 -0
  22. package/dist/pi-web-plugins/info/pi-web-plugin.js +51 -0
  23. package/dist/pi-web-plugins/updates/package.json +9 -0
  24. package/dist/pi-web-plugins/updates/pi-web-plugin.js +181 -0
  25. package/dist/pi-web-plugins/workspace-tasks/config.js +91 -0
  26. package/dist/pi-web-plugins/workspace-tasks/package.json +9 -0
  27. package/dist/pi-web-plugins/workspace-tasks/pi-web-plugin.js +48 -0
  28. package/dist/pi-web-plugins/workspace-tasks/taskRunner.js +12 -0
  29. package/dist/pi-web-plugins/workspace-tasks/tasksPanelElement.js +292 -0
  30. package/dist/pi-web-plugins/workspace-tasks/workspaceTasksClient.js +47 -0
  31. package/dist/piWebVersionReport.js +221 -0
  32. package/dist/piWebVersionReport.js.map +1 -0
  33. package/dist/plugin-api/unstable.d.ts +22 -0
  34. package/dist/plugin-api.d.ts +163 -0
  35. package/dist/server/activity/workspaceActivityRoutes.js +4 -0
  36. package/dist/server/activity/workspaceActivityRoutes.js.map +1 -0
  37. package/dist/server/activity/workspaceActivityService.js +98 -0
  38. package/dist/server/activity/workspaceActivityService.js.map +1 -0
  39. package/dist/server/app.js +115 -0
  40. package/dist/server/app.js.map +1 -0
  41. package/dist/server/configRoutes.js +123 -0
  42. package/dist/server/configRoutes.js.map +1 -0
  43. package/dist/server/diagnostics/nodePtySpawnHelper.js +135 -0
  44. package/dist/server/diagnostics/nodePtySpawnHelper.js.map +1 -0
  45. package/dist/server/git/gitEnv.js +15 -0
  46. package/dist/server/git/gitEnv.js.map +1 -0
  47. package/dist/server/git/gitService.js +119 -0
  48. package/dist/server/git/gitService.js.map +1 -0
  49. package/dist/server/gitRoutes.js +23 -0
  50. package/dist/server/gitRoutes.js.map +1 -0
  51. package/dist/server/index.js +7 -0
  52. package/dist/server/index.js.map +1 -0
  53. package/dist/server/machines/machineClient.js +134 -0
  54. package/dist/server/machines/machineClient.js.map +1 -0
  55. package/dist/server/machines/machineProxyRoutes.js +92 -0
  56. package/dist/server/machines/machineProxyRoutes.js.map +1 -0
  57. package/dist/server/machines/machineRoutes.js +50 -0
  58. package/dist/server/machines/machineRoutes.js.map +1 -0
  59. package/dist/server/machines/machineService.js +168 -0
  60. package/dist/server/machines/machineService.js.map +1 -0
  61. package/dist/server/machines/machineStore.js +128 -0
  62. package/dist/server/machines/machineStore.js.map +1 -0
  63. package/dist/server/piWebPluginService.js +235 -0
  64. package/dist/server/piWebPluginService.js.map +1 -0
  65. package/dist/server/piWebStatus.js +462 -0
  66. package/dist/server/piWebStatus.js.map +1 -0
  67. package/dist/server/projects/directorySuggestions.js +37 -0
  68. package/dist/server/projects/directorySuggestions.js.map +1 -0
  69. package/dist/server/projects/projectService.js +31 -0
  70. package/dist/server/projects/projectService.js.map +1 -0
  71. package/dist/server/realtime/sessionEventHub.js +39 -0
  72. package/dist/server/realtime/sessionEventHub.js.map +1 -0
  73. package/dist/server/sessiond/sessionProxyRoutes.js +60 -0
  74. package/dist/server/sessiond/sessionProxyRoutes.js.map +1 -0
  75. package/dist/server/sessiond.js +61 -0
  76. package/dist/server/sessiond.js.map +1 -0
  77. package/dist/server/sessions/authProviderOptions.js +56 -0
  78. package/dist/server/sessions/authProviderOptions.js.map +1 -0
  79. package/dist/server/sessions/authRoutes.js +59 -0
  80. package/dist/server/sessions/authRoutes.js.map +1 -0
  81. package/dist/server/sessions/authService.js +74 -0
  82. package/dist/server/sessions/authService.js.map +1 -0
  83. package/dist/server/sessions/builtinCommands.js +27 -0
  84. package/dist/server/sessions/builtinCommands.js.map +1 -0
  85. package/dist/server/sessions/editPreview.js +196 -0
  86. package/dist/server/sessions/editPreview.js.map +1 -0
  87. package/dist/server/sessions/messagePaging.js +43 -0
  88. package/dist/server/sessions/messagePaging.js.map +1 -0
  89. package/dist/server/sessions/oauthLoginFlowService.js +219 -0
  90. package/dist/server/sessions/oauthLoginFlowService.js.map +1 -0
  91. package/dist/server/sessions/piSessionService.js +1054 -0
  92. package/dist/server/sessions/piSessionService.js.map +1 -0
  93. package/dist/server/sessions/sessionArchiveStore.js +216 -0
  94. package/dist/server/sessions/sessionArchiveStore.js.map +1 -0
  95. package/dist/server/sessions/sessionArchiveTree.js +35 -0
  96. package/dist/server/sessions/sessionArchiveTree.js.map +1 -0
  97. package/dist/server/sessions/sessionCommandService.js +234 -0
  98. package/dist/server/sessions/sessionCommandService.js.map +1 -0
  99. package/dist/server/sessions/sessionNameGenerator.js +68 -0
  100. package/dist/server/sessions/sessionNameGenerator.js.map +1 -0
  101. package/dist/server/sessions/sessionRoutes.js +184 -0
  102. package/dist/server/sessions/sessionRoutes.js.map +1 -0
  103. package/dist/server/sessions/sessionRuntimeStore.js +2 -0
  104. package/dist/server/sessions/sessionRuntimeStore.js.map +1 -0
  105. package/dist/server/storage/projectStore.js +88 -0
  106. package/dist/server/storage/projectStore.js.map +1 -0
  107. package/dist/server/terminalProxyRoutes.js +130 -0
  108. package/dist/server/terminalProxyRoutes.js.map +1 -0
  109. package/dist/server/terminals/terminalRoutes.js +138 -0
  110. package/dist/server/terminals/terminalRoutes.js.map +1 -0
  111. package/dist/server/terminals/terminalService.js +293 -0
  112. package/dist/server/terminals/terminalService.js.map +1 -0
  113. package/dist/server/terminals/terminalSize.js +17 -0
  114. package/dist/server/terminals/terminalSize.js.map +1 -0
  115. package/dist/server/types.js +2 -0
  116. package/dist/server/types.js.map +1 -0
  117. package/dist/server/webSocketBridge.js +32 -0
  118. package/dist/server/webSocketBridge.js.map +1 -0
  119. package/dist/server/workspaceExplorerRoutes.js +42 -0
  120. package/dist/server/workspaceExplorerRoutes.js.map +1 -0
  121. package/dist/server/workspaces/fileContentService.js +70 -0
  122. package/dist/server/workspaces/fileContentService.js.map +1 -0
  123. package/dist/server/workspaces/fileSuggestions.js +148 -0
  124. package/dist/server/workspaces/fileSuggestions.js.map +1 -0
  125. package/dist/server/workspaces/fileTreeService.js +26 -0
  126. package/dist/server/workspaces/fileTreeService.js.map +1 -0
  127. package/dist/server/workspaces/gitWorktreeDiscovery.js +34 -0
  128. package/dist/server/workspaces/gitWorktreeDiscovery.js.map +1 -0
  129. package/dist/server/workspaces/imagePreviewService.js +40 -0
  130. package/dist/server/workspaces/imagePreviewService.js.map +1 -0
  131. package/dist/server/workspaces/pathSafety.js +45 -0
  132. package/dist/server/workspaces/pathSafety.js.map +1 -0
  133. package/dist/server/workspaces/workspaceContext.js +8 -0
  134. package/dist/server/workspaces/workspaceContext.js.map +1 -0
  135. package/dist/server/workspaces/workspaceService.js +39 -0
  136. package/dist/server/workspaces/workspaceService.js.map +1 -0
  137. package/dist/sessiond/config.js +9 -0
  138. package/dist/sessiond/config.js.map +1 -0
  139. package/dist/sessiond/sessionDaemonClient.js +65 -0
  140. package/dist/sessiond/sessionDaemonClient.js.map +1 -0
  141. package/dist/shared/activity.js +26 -0
  142. package/dist/shared/activity.js.map +1 -0
  143. package/dist/shared/apiTypes.d.ts +464 -0
  144. package/dist/shared/apiTypes.js +2 -0
  145. package/dist/shared/apiTypes.js.map +1 -0
  146. package/dist/shared/federatedRoutes.js +57 -0
  147. package/dist/shared/federatedRoutes.js.map +1 -0
  148. package/dist/shared/piWebStatusParsing.js +62 -0
  149. package/dist/shared/piWebStatusParsing.js.map +1 -0
  150. package/dist/shared/pluginIds.js +5 -0
  151. package/dist/shared/pluginIds.js.map +1 -0
  152. package/dist/shared/workspaceFiles.js +3 -0
  153. package/dist/shared/workspaceFiles.js.map +1 -0
  154. package/docs/assets/favicon.svg +11 -0
  155. package/docs/assets/pi-web-banner.png +0 -0
  156. package/docs/assets/pi-web-demo.gif +0 -0
  157. package/docs/assets/pi-web-demo.webm +0 -0
  158. package/docs/plugins.md +762 -0
  159. package/extensions/pi-web.ts +133 -0
  160. package/install.sh +5 -0
  161. package/package.json +127 -0
  162. package/plugin-api/unstable.d.ts +1 -0
  163. package/plugin-api.d.ts +1 -0
@@ -0,0 +1,11 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024" role="img" aria-labelledby="title desc">
2
+ <title id="title">PI WEB mark</title>
3
+ <desc id="desc">A horizontal brand bar.</desc>
4
+ <style>
5
+ .bar { fill: #00f0d8; }
6
+ @media (prefers-color-scheme: light) {
7
+ .bar { fill: #008c82; }
8
+ }
9
+ </style>
10
+ <rect class="bar" x="128" y="448" width="768" height="128"/>
11
+ </svg>
@@ -0,0 +1,60 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
+ <title>PI WEB</title>
7
+ <meta name="theme-color" content="#0d1117" />
8
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
9
+ <link rel="apple-touch-icon" href="/apple-touch-icon.png" />
10
+ <link rel="manifest" href="/manifest.webmanifest" />
11
+ <style>
12
+ :root {
13
+ color-scheme: dark;
14
+ --pi-bg: #0d1117;
15
+ --pi-surface: #161b22;
16
+ --pi-surface-hover: #21262d;
17
+ --pi-terminal-bg: #05070a;
18
+ --pi-terminal-text: #e6edf3;
19
+ --pi-border: #30363d;
20
+ --pi-border-muted: #21262d;
21
+ --pi-text: #e6edf3;
22
+ --pi-text-secondary: #c9d1d9;
23
+ --pi-text-bright: #f0f6fc;
24
+ --pi-muted: #8b949e;
25
+ --pi-dim: #6e7681;
26
+ --pi-accent: #58a6ff;
27
+ --pi-accent-border: #2f81f7;
28
+ --pi-selection-bg: #0d2847;
29
+ --pi-success: #3fb950;
30
+ --pi-success-border: #238636;
31
+ --pi-success-bg: #0f1b12;
32
+ --pi-success-surface: #0f2a16;
33
+ --pi-success-ring: #3fb95055;
34
+ --pi-warning: #d29922;
35
+ --pi-warning-border: #6e5200;
36
+ --pi-warning-surface: #1f1a10;
37
+ --pi-danger: #ff7b72;
38
+ --pi-purple: #d2a8ff;
39
+ --pi-purple-border: #a371f7;
40
+ --pi-purple-surface: #21132f;
41
+ --pi-overlay: #0008;
42
+ --pi-shadow-soft: #0006;
43
+ --pi-shadow: #0008;
44
+ --pi-shadow-strong: #000b;
45
+ --pi-bg-overlay-soft: #0d1117dd;
46
+ --pi-bg-overlay: #0d1117e6;
47
+ --pi-success-bg-overlay: #0f1b12ee;
48
+ --pi-terminal-selection: #264f78;
49
+ }
50
+ html, body { margin: 0; height: 100%; overflow: hidden; background: var(--pi-bg); color: var(--pi-text); }
51
+ body { min-height: 100dvh; }
52
+ </style>
53
+ <script type="module" crossorigin src="/assets/index-BjUH4a8R.js"></script>
54
+ <link rel="modulepreload" crossorigin href="/assets/vendor-editor-core-B4Sq6exx.js">
55
+ <link rel="modulepreload" crossorigin href="/assets/vendor-editor-languages-DznYbTkJ.js">
56
+ </head>
57
+ <body>
58
+ <pi-web-app></pi-web-app>
59
+ </body>
60
+ </html>
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "PI WEB",
3
+ "short_name": "PI WEB",
4
+ "description": "Remote web UI and browser control plane for persistent Pi Coding Agent sessions.",
5
+ "start_url": "/",
6
+ "scope": "/",
7
+ "display": "standalone",
8
+ "background_color": "#0d1117",
9
+ "theme_color": "#0d1117",
10
+ "icons": [
11
+ {
12
+ "src": "/pwa-icon-192.png",
13
+ "sizes": "192x192",
14
+ "type": "image/png",
15
+ "purpose": "any maskable"
16
+ },
17
+ {
18
+ "src": "/pwa-icon-512.png",
19
+ "sizes": "512x512",
20
+ "type": "image/png",
21
+ "purpose": "any maskable"
22
+ }
23
+ ]
24
+ }
Binary file
Binary file
package/dist/config.js ADDED
@@ -0,0 +1,154 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { homedir } from "node:os";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { isPiWebPluginId, piWebPluginIdPattern } from "./shared/pluginIds.js";
5
+ export function defaultPiWebConfigPath(env = process.env) {
6
+ const xdgConfigHome = env["XDG_CONFIG_HOME"];
7
+ return join(xdgConfigHome !== undefined && xdgConfigHome !== "" ? xdgConfigHome : join(homedir(), ".config"), "pi-web", "config.json");
8
+ }
9
+ export function defaultPiWebDataDir() {
10
+ return join(homedir(), ".pi-web");
11
+ }
12
+ export function piWebDataDir(env = process.env, cwd = process.cwd()) {
13
+ const configured = env["PI_WEB_DATA_DIR"];
14
+ if (configured === undefined || configured === "")
15
+ return defaultPiWebDataDir();
16
+ return resolve(cwd, configured);
17
+ }
18
+ export function piWebConfigPath(env = process.env, cwd = process.cwd()) {
19
+ const configured = env["PI_WEB_CONFIG"];
20
+ if (configured === undefined || configured === "")
21
+ return defaultPiWebConfigPath(env);
22
+ return resolve(cwd, configured);
23
+ }
24
+ export function loadPiWebConfig(options = {}) {
25
+ const env = options.env ?? process.env;
26
+ const path = piWebConfigPath(env, options.cwd ?? process.cwd());
27
+ if (!existsSync(path))
28
+ return { path, exists: false, config: {} };
29
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
30
+ if (!isRecord(parsed))
31
+ throw new Error(`PI WEB config must be a JSON object: ${path}`);
32
+ return { path, exists: true, config: parsePiWebConfig(parsed, path) };
33
+ }
34
+ export function effectivePiWebConfig(options = {}) {
35
+ const loaded = loadPiWebConfig(options);
36
+ const env = options.env ?? process.env;
37
+ const host = env["PI_WEB_HOST"];
38
+ const port = env["PI_WEB_PORT"] ?? env["PORT"];
39
+ const allowedHosts = env["PI_WEB_ALLOWED_HOSTS"];
40
+ return {
41
+ ...loaded,
42
+ config: {
43
+ ...loaded.config,
44
+ ...(host !== undefined && host !== "" ? { host } : {}),
45
+ ...(port !== undefined && port !== "" ? { port: parsePort(port, "PI_WEB_PORT") } : {}),
46
+ ...(allowedHosts !== undefined && allowedHosts !== "" ? { allowedHosts: parseAllowedHostsEnv(allowedHosts) } : {}),
47
+ },
48
+ };
49
+ }
50
+ export function savePiWebConfig(config, options = {}) {
51
+ const env = options.env ?? process.env;
52
+ const path = piWebConfigPath(env, options.cwd ?? process.cwd());
53
+ const normalized = parsePiWebConfig(piWebConfigRecord(config), path);
54
+ const existing = readExistingConfigObject(path);
55
+ delete existing["host"];
56
+ delete existing["port"];
57
+ delete existing["allowedHosts"];
58
+ delete existing["shortcuts"];
59
+ delete existing["plugins"];
60
+ const merged = { ...existing, ...piWebConfigRecord(normalized) };
61
+ mkdirSync(dirname(path), { recursive: true });
62
+ writeFileSync(path, `${JSON.stringify(merged, null, 2)}\n`, "utf8");
63
+ return { path, exists: true, config: normalized };
64
+ }
65
+ function readExistingConfigObject(path) {
66
+ if (!existsSync(path))
67
+ return {};
68
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
69
+ if (!isRecord(parsed))
70
+ throw new Error(`PI WEB config must be a JSON object: ${path}`);
71
+ return parsed;
72
+ }
73
+ function piWebConfigRecord(config) {
74
+ return {
75
+ ...(config.host !== undefined ? { host: config.host } : {}),
76
+ ...(config.port !== undefined ? { port: config.port } : {}),
77
+ ...(config.allowedHosts !== undefined ? { allowedHosts: config.allowedHosts } : {}),
78
+ ...(config.shortcuts !== undefined ? { shortcuts: config.shortcuts } : {}),
79
+ ...(config.plugins !== undefined ? { plugins: config.plugins } : {}),
80
+ };
81
+ }
82
+ function parsePiWebConfig(value, path) {
83
+ return {
84
+ ...(value["host"] !== undefined ? { host: parseString(value["host"], "host", path) } : {}),
85
+ ...(value["port"] !== undefined ? { port: parsePort(value["port"], "port", path) } : {}),
86
+ ...(value["allowedHosts"] !== undefined ? { allowedHosts: parseAllowedHosts(value["allowedHosts"], path) } : {}),
87
+ ...(value["shortcuts"] !== undefined ? { shortcuts: parseShortcuts(value["shortcuts"], path) } : {}),
88
+ ...(value["plugins"] !== undefined ? { plugins: parsePlugins(value["plugins"], path) } : {}),
89
+ };
90
+ }
91
+ function parseString(value, key, path) {
92
+ if (typeof value !== "string" || value === "")
93
+ throw new Error(`PI WEB config ${key} must be a non-empty string: ${path}`);
94
+ return value;
95
+ }
96
+ function parsePort(value, key, path = "environment") {
97
+ const port = typeof value === "number" ? value : typeof value === "string" && value !== "" ? Number(value) : NaN;
98
+ if (!Number.isInteger(port) || port < 1 || port > 65535)
99
+ throw new Error(`PI WEB config ${key} must be an integer from 1 to 65535: ${path}`);
100
+ return port;
101
+ }
102
+ function parseAllowedHosts(value, path) {
103
+ if (value === true)
104
+ return true;
105
+ if (!isNonEmptyStringArray(value)) {
106
+ throw new Error(`PI WEB config allowedHosts must be true or an array of non-empty strings: ${path}`);
107
+ }
108
+ return value;
109
+ }
110
+ function parseAllowedHostsEnv(value) {
111
+ if (value === "true")
112
+ return true;
113
+ return value.split(",").map((host) => host.trim()).filter((host) => host !== "");
114
+ }
115
+ function parseShortcuts(value, path) {
116
+ if (!isRecord(value))
117
+ throw new Error(`PI WEB config shortcuts must be an object: ${path}`);
118
+ return Object.fromEntries(Object.entries(value).map(([actionId, shortcut]) => {
119
+ if (shortcut !== null && (typeof shortcut !== "string" || shortcut === "")) {
120
+ throw new Error(`PI WEB config shortcut values must be non-empty strings or null: ${path}`);
121
+ }
122
+ return [actionId, shortcut];
123
+ }));
124
+ }
125
+ function parsePlugins(value, path) {
126
+ if (!isRecord(value) || Array.isArray(value))
127
+ throw new Error(`PI WEB config plugins must be an object: ${path}`);
128
+ return Object.fromEntries(Object.entries(value).map(([pluginId, config]) => {
129
+ if (!isPiWebPluginId(pluginId))
130
+ throw new Error(`PI WEB config plugin ids must match ${piWebPluginIdPattern.source}: ${path}`);
131
+ if (!isRecord(config) || Array.isArray(config))
132
+ throw new Error(`PI WEB config plugin entries must be objects: ${path}`);
133
+ const enabled = config["enabled"];
134
+ if (enabled !== undefined && typeof enabled !== "boolean")
135
+ throw new Error(`PI WEB config plugin enabled values must be booleans: ${path}`);
136
+ const settings = config["settings"];
137
+ if (settings !== undefined && (!isRecord(settings) || Array.isArray(settings)))
138
+ throw new Error(`PI WEB config plugin settings must be objects: ${path}`);
139
+ return [pluginId, config];
140
+ }));
141
+ }
142
+ function isRecord(value) {
143
+ return typeof value === "object" && value !== null && !Array.isArray(value);
144
+ }
145
+ function isNonEmptyStringArray(value) {
146
+ return Array.isArray(value) && value.every((item) => typeof item === "string" && item !== "");
147
+ }
148
+ export function examplePiWebConfig(config = {}) {
149
+ return `${JSON.stringify({ host: config.host ?? "127.0.0.1", port: config.port ?? 8504, allowedHosts: config.allowedHosts ?? [] }, null, 2)}\n`;
150
+ }
151
+ export function piWebConfigDir(env = process.env) {
152
+ return dirname(defaultPiWebConfigPath(env));
153
+ }
154
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@pi-web/info-plugin",
3
+ "private": true,
4
+ "piWeb": {
5
+ "plugins": [
6
+ { "id": "info", "module": "pi-web-plugin.js" }
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,51 @@
1
+ // Generated from pi-web-plugins\info\pi-web-plugin.ts. Do not edit directly.
2
+ const plugin = {
3
+ apiVersion: 1,
4
+ name: "信息插件",
5
+ activate: ({ html, svg }) => ({
6
+ contributions: {
7
+ actions: [
8
+ {
9
+ id: "workspace.show-path",
10
+ title: "显示当前工作区路径",
11
+ group: "信息",
12
+ enabled: (context) => context.state.selectedWorkspace !== undefined,
13
+ run: (context) => {
14
+ const path = context.state.selectedWorkspace?.path ?? "未选择工作区";
15
+ window.alert(path);
16
+ },
17
+ },
18
+ ],
19
+ workspaceLabels: [
20
+ {
21
+ id: "workspace.kind-label",
22
+ order: 100,
23
+ items: (context) => [{ type: "text", text: context.workspace.isGitRepo ? "git" : "文件夹", title: context.workspace.path }],
24
+ },
25
+ ],
26
+ workspacePanels: [
27
+ {
28
+ id: "workspace.info",
29
+ title: "信息",
30
+ icon: svg `
31
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
32
+ <circle cx="12" cy="12" r="9"></circle>
33
+ <path d="M12 11v5"></path>
34
+ <path d="M12 8h.01"></path>
35
+ </svg>
36
+ `,
37
+ order: 1000,
38
+ render: (context) => html `
39
+ <section class="toolbar"><strong>信息</strong></section>
40
+ <section class="viewer">
41
+ <p><strong>工作区</strong></p>
42
+ <p class="muted">${context.workspace.label}</p>
43
+ <p class="muted">${context.workspace.path}</p>
44
+ </section>
45
+ `,
46
+ },
47
+ ],
48
+ },
49
+ }),
50
+ };
51
+ export default plugin;
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@pi-web/updates-plugin",
3
+ "private": true,
4
+ "piWeb": {
5
+ "plugins": [
6
+ { "id": "updates", "module": "pi-web-plugin.js" }
7
+ ]
8
+ }
9
+ }
@@ -0,0 +1,181 @@
1
+ // Generated from pi-web-plugins\updates\pi-web-plugin.ts. Do not edit directly.
2
+ function messagesFor(state) {
3
+ return state?.piWebStatus?.messages ?? [];
4
+ }
5
+ function statusFor(state) {
6
+ return state?.piWebStatus;
7
+ }
8
+ function messageCount(state) {
9
+ return messagesFor(state).length;
10
+ }
11
+ function isLocalOrUnknownInstallation(installation) {
12
+ return installation === undefined || installation.kind === "local" || installation.kind === "unknown";
13
+ }
14
+ function shouldShowUpdatesPanel(state) {
15
+ const status = statusFor(state);
16
+ if (messageCount(state) > 0)
17
+ return true;
18
+ if (status === undefined)
19
+ return false;
20
+ return isLocalOrUnknownInstallation(status.components.web.installation)
21
+ || isLocalOrUnknownInstallation(status.components.sessiond.installation);
22
+ }
23
+ function formatVersion(version) {
24
+ return version === undefined || version === "" ? "未知" : version;
25
+ }
26
+ function installationLabel(installation) {
27
+ if (installation === undefined)
28
+ return "安装来源未知";
29
+ if (installation.kind === "pi-package") {
30
+ const scope = installation.scope === undefined ? "" : ` · ${installation.scope}`;
31
+ const source = installation.source ?? "Pi 包";
32
+ return `${source}${scope}`;
33
+ }
34
+ if (installation.kind === "npm-global")
35
+ return "全局 npm 包";
36
+ if (installation.kind === "local")
37
+ return "本地检出";
38
+ return "安装来源未知";
39
+ }
40
+ function componentLabel(label) {
41
+ if (label === "Web")
42
+ return "Web/UI";
43
+ if (label === "Session daemon")
44
+ return "会话守护进程";
45
+ return label;
46
+ }
47
+ function severityLabel(severity) {
48
+ if (severity === "error")
49
+ return "错误";
50
+ if (severity === "warning")
51
+ return "警告";
52
+ return "信息";
53
+ }
54
+ function renderComponent(html, component) {
55
+ const status = !component.available
56
+ ? "不可用"
57
+ : component.stale
58
+ ? "需要重启"
59
+ : "当前版本";
60
+ return html `
61
+ <div class="updates-version-row">
62
+ <strong>${componentLabel(component.label)}</strong>
63
+ <span>${status}</span>
64
+ <small>运行中 ${formatVersion(component.runtimeVersion)} · 已安装 ${formatVersion(component.installedVersion)}</small>
65
+ <small>${installationLabel(component.installation)}${component.installation?.path === undefined ? "" : ` · ${component.installation.path}`}</small>
66
+ </div>
67
+ `;
68
+ }
69
+ function renderCommand(html, label, command) {
70
+ return html `
71
+ <div class="updates-command">
72
+ <span>${label}</span>
73
+ <code>${command}</code>
74
+ <button @click=${() => { void navigator.clipboard.writeText(command); }}>复制</button>
75
+ </div>
76
+ `;
77
+ }
78
+ function renderCommands(html, status) {
79
+ const commands = [
80
+ ["更新", status.commands.update],
81
+ ["全部重启", status.commands.restart],
82
+ ["重启 Web/UI", status.commands.restartWeb],
83
+ ["重启会话守护进程", status.commands.restartSessiond],
84
+ ["状态", status.commands.status],
85
+ ].filter((entry) => typeof entry[1] === "string" && entry[1] !== "");
86
+ if (commands.length === 0)
87
+ return undefined;
88
+ return html `
89
+ <section>
90
+ <strong>建议命令</strong>
91
+ ${commands.map(([label, command]) => renderCommand(html, label, command))}
92
+ </section>
93
+ `;
94
+ }
95
+ function renderUpdatesPanel(html, state) {
96
+ const status = statusFor(state);
97
+ if (status === undefined) {
98
+ return html `
99
+ <section class="toolbar"><strong>更新</strong></section>
100
+ <section class="viewer"><p class="muted">正在检查 PI WEB 更新状态…</p></section>
101
+ `;
102
+ }
103
+ const messages = status.messages;
104
+ return html `
105
+ <style>
106
+ .viewer.updates-status { flex: 1 1 auto; min-height: 0; box-sizing: border-box; display: flex; flex-direction: column; gap: 14px; padding: 12px; overflow-y: auto; overflow-x: hidden; }
107
+ .viewer.updates-status section { flex: 0 0 auto; min-width: 0; display: grid; gap: 8px; }
108
+ .updates-message { display: grid; gap: 5px; border: 1px solid var(--pi-border); border-radius: 8px; padding: 10px; background: var(--pi-surface); }
109
+ .updates-message.warning { border-color: var(--pi-warning-border); background: var(--pi-warning-surface); }
110
+ .updates-message.error { border-color: var(--pi-danger); }
111
+ .updates-message-title { display: flex; gap: 8px; align-items: baseline; }
112
+ .updates-message-title span { color: var(--pi-muted); font-size: 12px; text-transform: uppercase; }
113
+ .updates-version-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 3px 10px; border-bottom: 1px solid var(--pi-border-muted); padding: 6px 0; }
114
+ .updates-version-row small { grid-column: 1 / -1; color: var(--pi-muted); }
115
+ .updates-command { min-width: 0; display: grid; grid-template-columns: minmax(90px, auto) minmax(0, 1fr) auto; gap: 8px; align-items: center; }
116
+ .updates-command code { overflow: auto; border: 1px solid var(--pi-border-muted); border-radius: 6px; background: var(--pi-bg); padding: 5px 7px; white-space: nowrap; }
117
+ .updates-meta { display: grid; gap: 2px; color: var(--pi-muted); font-size: 12px; }
118
+ @media (max-width: 520px) {
119
+ .updates-command { grid-template-columns: minmax(0, 1fr) auto; }
120
+ .updates-command > span { grid-column: 1 / -1; }
121
+ }
122
+ </style>
123
+ <section class="toolbar"><strong>更新</strong><span class="stale">测试版</span>${messages.length > 0 ? html `<span class="stale">${String(messages.length)}</span>` : null}</section>
124
+ <section class="viewer updates-status">
125
+ <section>
126
+ ${messages.length === 0 ? html `<p class="muted">没有 PI WEB 更新或重启消息。</p>` : messages.map((message) => html `
127
+ <article class=${`updates-message ${message.severity}`}>
128
+ <div class="updates-message-title"><strong>${message.title}</strong><span>${severityLabel(message.severity)}</span></div>
129
+ <p>${message.body}</p>
130
+ ${message.command === undefined ? null : html `<code>${message.command}</code>`}
131
+ </article>
132
+ `)}
133
+ </section>
134
+
135
+ <section>
136
+ <strong>已安装服务</strong>
137
+ ${renderComponent(html, status.components.web)}
138
+ ${renderComponent(html, status.components.sessiond)}
139
+ </section>
140
+
141
+ ${renderCommands(html, status)}
142
+
143
+ <section class="updates-meta">
144
+ <span>生成时间 ${status.generatedAt}</span>
145
+ ${status.release.latestVersion === undefined ? null : html `<span>最新 npm 版本 ${status.release.latestVersion}</span>`}
146
+ ${status.release.skipped === true ? html `<span>远程版本检查已跳过。</span>` : null}
147
+ ${status.release.error === undefined ? null : html `<span>远程版本检查失败:${status.release.error}</span>`}
148
+ </section>
149
+ </section>
150
+ `;
151
+ }
152
+ const plugin = {
153
+ apiVersion: 1,
154
+ name: "更新",
155
+ activate: ({ html, svg }) => ({
156
+ contributions: {
157
+ workspacePanels: [
158
+ {
159
+ id: "workspace.updates",
160
+ title: "更新",
161
+ icon: svg `
162
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
163
+ <path d="M20 6v5h-5"></path>
164
+ <path d="M4 18v-5h5"></path>
165
+ <path d="M18.4 9A7 7 0 0 0 6.1 6.7L4 8.8"></path>
166
+ <path d="M5.6 15A7 7 0 0 0 17.9 17.3L20 15.2"></path>
167
+ </svg>
168
+ `,
169
+ order: 100,
170
+ visible: (context) => shouldShowUpdatesPanel(context.state),
171
+ badge: (context) => {
172
+ const count = messageCount(context.state);
173
+ return html `测试版${count > 0 ? html ` · ${String(count)}` : null}`;
174
+ },
175
+ render: (context) => renderUpdatesPanel(html, context.state),
176
+ },
177
+ ],
178
+ },
179
+ }),
180
+ };
181
+ export default plugin;
@@ -0,0 +1,91 @@
1
+ // Generated from pi-web-plugins\workspace-tasks\config.ts. Do not edit directly.
2
+ export const TASKS_CONFIG_PATH = ".pi-web/tasks.json";
3
+ export const TASKS_CONFIG_VERSION = 1;
4
+ const taskIdPattern = /^[a-z][a-z0-9.-]*$/u;
5
+ export function parseTasksConfigText(text) {
6
+ let parsed;
7
+ try {
8
+ parsed = JSON.parse(text);
9
+ }
10
+ catch (error) {
11
+ return { ok: false, error: `JSON 无效: ${error instanceof Error ? error.message : String(error)}` };
12
+ }
13
+ return parseTasksConfig(parsed);
14
+ }
15
+ export function parseTasksConfig(value) {
16
+ if (!isRecord(value))
17
+ return invalid("配置必须是对象");
18
+ if (value["version"] !== TASKS_CONFIG_VERSION)
19
+ return invalid("配置 version 必须是 1");
20
+ const tasks = value["tasks"];
21
+ if (!Array.isArray(tasks))
22
+ return invalid("配置 tasks 必须是数组");
23
+ const ids = new Set();
24
+ const parsedTasks = [];
25
+ for (const [index, task] of tasks.entries()) {
26
+ const parsedTask = parseTask(task, index);
27
+ if (!parsedTask.ok)
28
+ return parsedTask;
29
+ if (ids.has(parsedTask.task.id))
30
+ return invalid(`任务 id 重复: ${parsedTask.task.id}`);
31
+ ids.add(parsedTask.task.id);
32
+ parsedTasks.push(parsedTask.task);
33
+ }
34
+ return { ok: true, config: { version: TASKS_CONFIG_VERSION, tasks: parsedTasks } };
35
+ }
36
+ function parseTask(value, index) {
37
+ const label = `任务 ${String(index + 1)}`;
38
+ if (!isRecord(value))
39
+ return invalid(`${label} 必须是对象`);
40
+ const id = requireNonEmptyString(value, "id", label);
41
+ if (!id.ok)
42
+ return id;
43
+ if (!taskIdPattern.test(id.value))
44
+ return invalid(`${label} id 必须匹配 ${taskIdPattern.source}`);
45
+ const title = requireNonEmptyString(value, "title", label);
46
+ if (!title.ok)
47
+ return title;
48
+ const command = requireNonEmptyString(value, "command", label);
49
+ if (!command.ok)
50
+ return command;
51
+ const description = optionalNonEmptyString(value, "description", label);
52
+ if (!description.ok)
53
+ return description;
54
+ const group = optionalNonEmptyString(value, "group", label);
55
+ if (!group.ok)
56
+ return group;
57
+ const confirm = value["confirm"];
58
+ if (confirm !== undefined && typeof confirm !== "boolean")
59
+ return invalid(`${label} confirm 必须是布尔值`);
60
+ return {
61
+ ok: true,
62
+ task: {
63
+ id: id.value,
64
+ title: title.value,
65
+ command: command.value,
66
+ ...(description.value === undefined ? {} : { description: description.value }),
67
+ ...(group.value === undefined ? {} : { group: group.value }),
68
+ confirm: confirm ?? false,
69
+ },
70
+ };
71
+ }
72
+ function requireNonEmptyString(record, key, label) {
73
+ const value = record[key];
74
+ if (typeof value !== "string" || value.trim() === "")
75
+ return invalid(`${label} ${key} 必须是非空字符串`);
76
+ return { ok: true, value };
77
+ }
78
+ function optionalNonEmptyString(record, key, label) {
79
+ const value = record[key];
80
+ if (value === undefined)
81
+ return { ok: true, value: undefined };
82
+ if (typeof value !== "string" || value.trim() === "")
83
+ return invalid(`${label} ${key} 提供时必须是非空字符串`);
84
+ return { ok: true, value };
85
+ }
86
+ function invalid(error) {
87
+ return { ok: false, error };
88
+ }
89
+ function isRecord(value) {
90
+ return typeof value === "object" && value !== null && !Array.isArray(value);
91
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "@pi-web/workspace-tasks-plugin",
3
+ "private": true,
4
+ "piWeb": {
5
+ "plugins": [
6
+ { "id": "workspace-tasks", "module": "pi-web-plugin.js" }
7
+ ]
8
+ }
9
+ }