pinokiod 7.2.6 → 7.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,390 @@
1
+ const path = require("path");
2
+
3
+ function createWorkspaceRuntimeService({ kernel }) {
4
+ if (!kernel) {
5
+ throw new Error("kernel is required");
6
+ }
7
+
8
+ const normalizePathKey = (value) => {
9
+ if (typeof value !== "string" || !value.trim()) {
10
+ return "";
11
+ }
12
+ const resolved = path.resolve(value.trim());
13
+ return process.platform === "win32" ? resolved.toLowerCase() : resolved;
14
+ };
15
+
16
+ const decodeMaybe = (value) => {
17
+ const raw = typeof value === "string" ? value.trim() : "";
18
+ if (!raw) {
19
+ return "";
20
+ }
21
+ try {
22
+ return decodeURIComponent(raw).trim();
23
+ } catch (_) {
24
+ return raw;
25
+ }
26
+ };
27
+
28
+ const resolveCandidatePath = (value) => {
29
+ const decoded = decodeMaybe(value);
30
+ if (!decoded || decoded.includes("\0")) {
31
+ return "";
32
+ }
33
+ if (decoded.startsWith("~/")) {
34
+ return path.resolve(kernel.homedir, decoded.slice(2));
35
+ }
36
+ if (path.isAbsolute(decoded)) {
37
+ return path.resolve(decoded);
38
+ }
39
+ return "";
40
+ };
41
+
42
+ const knownRoots = () => {
43
+ const roots = [];
44
+ const addRoot = (type, label) => {
45
+ if (!kernel || typeof kernel.path !== "function") {
46
+ return;
47
+ }
48
+ const rootPath = kernel.path(type);
49
+ if (typeof rootPath === "string" && rootPath.trim()) {
50
+ roots.push({
51
+ type,
52
+ label,
53
+ path: path.resolve(rootPath)
54
+ });
55
+ }
56
+ };
57
+ addRoot("workspaces", "Workspace");
58
+ addRoot("api", "App");
59
+ addRoot("plugin", "Plugin");
60
+ return roots;
61
+ };
62
+
63
+ const isPathWithin = (candidate, root) => {
64
+ if (!candidate || !root) {
65
+ return false;
66
+ }
67
+ const relative = path.relative(root, candidate);
68
+ return Boolean(relative && !relative.startsWith("..") && !path.isAbsolute(relative));
69
+ };
70
+
71
+ const resolveWorkspaceForPath = (candidatePath) => {
72
+ const candidate = resolveCandidatePath(candidatePath);
73
+ if (!candidate) {
74
+ return null;
75
+ }
76
+ for (const root of knownRoots()) {
77
+ if (!isPathWithin(candidate, root.path)) {
78
+ continue;
79
+ }
80
+ const relative = path.relative(root.path, candidate);
81
+ const segments = relative.split(path.sep).filter(Boolean);
82
+ const name = segments[0] || "";
83
+ if (!name) {
84
+ continue;
85
+ }
86
+ const cwd = path.resolve(root.path, name);
87
+ return {
88
+ key: normalizePathKey(cwd),
89
+ cwd,
90
+ name,
91
+ root: root.type,
92
+ rootLabel: root.label
93
+ };
94
+ }
95
+ return null;
96
+ };
97
+
98
+ const parseParamsFromText = (value) => {
99
+ const raw = typeof value === "string" ? value : "";
100
+ const index = raw.indexOf("?");
101
+ if (index < 0) {
102
+ return null;
103
+ }
104
+ try {
105
+ return new URLSearchParams(raw.slice(index + 1).replace(/&amp;/g, "&"));
106
+ } catch (_) {
107
+ return null;
108
+ }
109
+ };
110
+
111
+ const firstWorkspaceFromCandidates = (candidates) => {
112
+ for (const candidate of candidates) {
113
+ const workspace = resolveWorkspaceForPath(candidate);
114
+ if (workspace) {
115
+ return workspace;
116
+ }
117
+ }
118
+ return null;
119
+ };
120
+
121
+ const getShellCandidates = (shell) => {
122
+ const candidates = [];
123
+ if (!shell || typeof shell !== "object") {
124
+ return candidates;
125
+ }
126
+ const push = (value) => {
127
+ if (typeof value === "string" && value.trim()) {
128
+ candidates.push(value);
129
+ }
130
+ };
131
+ push(shell.path);
132
+ push(shell.group);
133
+ if (shell.params && typeof shell.params === "object") {
134
+ push(shell.params.cwd);
135
+ push(shell.params.path);
136
+ if (shell.params.$parent && typeof shell.params.$parent === "object") {
137
+ push(shell.params.$parent.cwd);
138
+ push(shell.params.$parent.path);
139
+ }
140
+ }
141
+ for (const text of [shell.id, shell.group]) {
142
+ const params = parseParamsFromText(text);
143
+ if (!params) {
144
+ continue;
145
+ }
146
+ push(params.get("cwd"));
147
+ push(params.get("path"));
148
+ }
149
+ return candidates;
150
+ };
151
+
152
+ const getScriptCandidates = (id) => {
153
+ const candidates = [];
154
+ const raw = typeof id === "string" ? id : "";
155
+ if (!raw) {
156
+ return candidates;
157
+ }
158
+ const params = parseParamsFromText(raw);
159
+ if (params) {
160
+ candidates.push(params.get("cwd"));
161
+ candidates.push(params.get("path"));
162
+ }
163
+ const pathPart = raw.split("?")[0];
164
+ candidates.push(pathPart);
165
+ return candidates;
166
+ };
167
+
168
+ const parseTerminalIdFromText = (value) => {
169
+ const params = parseParamsFromText(value);
170
+ if (!params) {
171
+ return "";
172
+ }
173
+ const terminalId = params.get("terminal_id");
174
+ return typeof terminalId === "string" ? terminalId.trim() : "";
175
+ };
176
+
177
+ const buildShellUrl = (shell) => {
178
+ const raw = shell && typeof shell.id === "string" ? shell.id.trim() : "";
179
+ if (!raw) {
180
+ return "";
181
+ }
182
+ const index = raw.indexOf("?");
183
+ const base = index >= 0 ? raw.slice(0, index) : raw;
184
+ const query = index >= 0 ? raw.slice(index + 1) : "";
185
+ const params = new URLSearchParams(query);
186
+ if (!params.has("path") && shell && typeof shell.path === "string" && shell.path.trim()) {
187
+ params.set("path", shell.path.trim());
188
+ }
189
+ if (!params.has("terminal_id") && shell && typeof shell.terminal_id === "string" && shell.terminal_id.trim()) {
190
+ params.set("terminal_id", shell.terminal_id.trim());
191
+ }
192
+ if (!params.has("input")) {
193
+ params.set("input", "1");
194
+ }
195
+ const queryString = params.toString();
196
+ let route = "";
197
+ if (base.startsWith("/shell/")) {
198
+ const shellId = base.slice("/shell/".length);
199
+ route = shellId.includes("/")
200
+ ? `/shell/${encodeURIComponent(shellId)}`
201
+ : base;
202
+ } else if (base.startsWith("shell/")) {
203
+ route = `/shell/${encodeURIComponent(base.slice("shell/".length))}`;
204
+ } else {
205
+ route = `/shell/${encodeURIComponent(base)}`;
206
+ }
207
+ return queryString ? `${route}?${queryString}` : route;
208
+ };
209
+
210
+ const buildRunUrl = (id) => {
211
+ const raw = typeof id === "string" ? id.trim() : "";
212
+ if (!raw) {
213
+ return "";
214
+ }
215
+ const index = raw.indexOf("?");
216
+ const scriptPath = resolveCandidatePath(index >= 0 ? raw.slice(0, index) : raw);
217
+ const query = index >= 0 ? raw.slice(index + 1) : "";
218
+ if (!scriptPath) {
219
+ return "";
220
+ }
221
+ const roots = [
222
+ { name: "api", path: kernel.path("api") },
223
+ { name: "plugin", path: kernel.path("plugin") },
224
+ { name: "scripts", path: kernel.path("scripts") }
225
+ ].filter((root) => typeof root.path === "string" && root.path.trim());
226
+ for (const root of roots) {
227
+ const rootPath = path.resolve(root.path);
228
+ const relative = path.relative(rootPath, scriptPath);
229
+ if (!relative || relative.startsWith("..") || path.isAbsolute(relative)) {
230
+ continue;
231
+ }
232
+ const params = new URLSearchParams(query);
233
+ if (!params.has("chrome")) {
234
+ params.set("chrome", "full");
235
+ }
236
+ const route = relative.split(path.sep).map(encodeURIComponent).join("/");
237
+ const queryString = params.toString();
238
+ return `/run/${root.name}/${route}${queryString ? `?${queryString}` : ""}`;
239
+ }
240
+ return "";
241
+ };
242
+
243
+ const shellTitle = (shell) => {
244
+ if (shell && shell.params && typeof shell.params.$title === "string" && shell.params.$title.trim()) {
245
+ return shell.params.$title.trim();
246
+ }
247
+ if (shell && typeof shell.cmd === "string" && shell.cmd.trim()) {
248
+ return shell.cmd.trim().slice(0, 120);
249
+ }
250
+ return "Terminal";
251
+ };
252
+
253
+ const scriptTitle = (id) => {
254
+ const raw = typeof id === "string" ? id.trim() : "";
255
+ if (!raw) {
256
+ return "Script";
257
+ }
258
+ const pathPart = raw.split("?")[0];
259
+ if (pathPart && path.isAbsolute(pathPart)) {
260
+ return path.basename(pathPart) || "Script";
261
+ }
262
+ return raw.slice(0, 120);
263
+ };
264
+
265
+ const createGroup = (workspace) => ({
266
+ cwd: workspace.cwd,
267
+ name: workspace.name,
268
+ root: workspace.root,
269
+ rootLabel: workspace.rootLabel,
270
+ running: true,
271
+ shells: [],
272
+ scripts: []
273
+ });
274
+
275
+ const list = () => {
276
+ const groups = new Map();
277
+ const unscoped = {
278
+ shells: [],
279
+ scripts: []
280
+ };
281
+ const getGroup = (workspace) => {
282
+ if (!workspace || !workspace.key) {
283
+ return null;
284
+ }
285
+ if (!groups.has(workspace.key)) {
286
+ groups.set(workspace.key, createGroup(workspace));
287
+ }
288
+ return groups.get(workspace.key);
289
+ };
290
+
291
+ const shells = kernel.shell && Array.isArray(kernel.shell.shells)
292
+ ? kernel.shell.shells
293
+ : [];
294
+ for (const shell of shells) {
295
+ if (!shell || shell.done === true || !shell.ptyProcess) {
296
+ continue;
297
+ }
298
+ const item = {
299
+ id: typeof shell.id === "string" ? shell.id : "",
300
+ group: typeof shell.group === "string" ? shell.group : "",
301
+ title: shellTitle(shell),
302
+ cwd: typeof shell.path === "string" ? shell.path : "",
303
+ state: shell.state || null,
304
+ start_time: Number.isFinite(shell.start_time) ? shell.start_time : null,
305
+ terminal_id: shell.terminal_id || parseTerminalIdFromText(shell.id) || parseTerminalIdFromText(shell.group) || null,
306
+ url: buildRunUrl(shell.group) || buildShellUrl(shell)
307
+ };
308
+ const workspace = firstWorkspaceFromCandidates(getShellCandidates(shell));
309
+ const group = getGroup(workspace);
310
+ if (group) {
311
+ group.shells.push(item);
312
+ } else {
313
+ unscoped.shells.push(item);
314
+ }
315
+ }
316
+
317
+ const running = kernel.api && kernel.api.running && typeof kernel.api.running === "object"
318
+ ? kernel.api.running
319
+ : {};
320
+ for (const id of Object.keys(running)) {
321
+ if (typeof id !== "string" || !id || id.startsWith("shell/")) {
322
+ continue;
323
+ }
324
+ const item = {
325
+ id,
326
+ title: scriptTitle(id),
327
+ path: id.split("?")[0],
328
+ cwd: "",
329
+ url: buildRunUrl(id)
330
+ };
331
+ const workspace = firstWorkspaceFromCandidates(getScriptCandidates(id));
332
+ if (workspace) {
333
+ item.cwd = workspace.cwd;
334
+ }
335
+ const group = getGroup(workspace);
336
+ if (group) {
337
+ group.scripts.push(item);
338
+ } else {
339
+ unscoped.scripts.push(item);
340
+ }
341
+ }
342
+
343
+ const workspaces = Array.from(groups.values())
344
+ .map((workspace) => ({
345
+ ...workspace,
346
+ counts: {
347
+ shells: workspace.shells.length,
348
+ scripts: workspace.scripts.length
349
+ }
350
+ }))
351
+ .sort((a, b) => {
352
+ const totalA = a.counts.shells + a.counts.scripts;
353
+ const totalB = b.counts.shells + b.counts.scripts;
354
+ if (totalA !== totalB) {
355
+ return totalB - totalA;
356
+ }
357
+ return String(a.name || "").localeCompare(String(b.name || ""));
358
+ });
359
+
360
+ return {
361
+ workspaces,
362
+ unscoped,
363
+ counts: {
364
+ workspaces: workspaces.length,
365
+ shells: workspaces.reduce((total, workspace) => total + workspace.counts.shells, 0) + unscoped.shells.length,
366
+ scripts: workspaces.reduce((total, workspace) => total + workspace.counts.scripts, 0) + unscoped.scripts.length
367
+ }
368
+ };
369
+ };
370
+
371
+ const summary = () => {
372
+ const runtime = list();
373
+ return {
374
+ runningWorkspaces: runtime.counts.workspaces,
375
+ runningShells: runtime.counts.shells,
376
+ runningScripts: runtime.counts.scripts,
377
+ unscopedShells: runtime.unscoped.shells.length,
378
+ unscopedScripts: runtime.unscoped.scripts.length
379
+ };
380
+ };
381
+
382
+ return {
383
+ list,
384
+ summary
385
+ };
386
+ }
387
+
388
+ module.exports = {
389
+ createWorkspaceRuntimeService
390
+ };
@@ -2328,6 +2328,14 @@ if (typeof hotkeys === 'function') {
2328
2328
  playNextSound();
2329
2329
  };
2330
2330
 
2331
+ window.PinokioPlayNotificationSound = (sound) => {
2332
+ if (sound === false || isFalseyString(sound)) {
2333
+ return false;
2334
+ }
2335
+ enqueueSound(typeof sound === 'string' && sound ? sound : '/chime.mp3');
2336
+ return true;
2337
+ };
2338
+
2331
2339
  const handlePacket = (packet) => {
2332
2340
  if (!packet || packet.id !== CHANNEL_ID || packet.type !== 'notification') {
2333
2341
  return;