pinokiod 7.1.72 → 7.1.73

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.
@@ -4,7 +4,7 @@ const semver = require('semver')
4
4
 
5
5
  class Bluefairy {
6
6
  description = "Installs Bluefairy, a standalone package freshness guard."
7
- version = ">=0.0.27"
7
+ version = ">=0.0.28"
8
8
 
9
9
  packageName() {
10
10
  return "bluefairy"
@@ -51,11 +51,15 @@ class Bluefairy {
51
51
  path.resolve(binDir, "bluefairy-activate"),
52
52
  path.resolve(binDir, "bluefairy-activate.cmd"),
53
53
  path.resolve(binDir, "bluefairy-activate.ps1"),
54
+ path.resolve(binDir, "bluefairy-deactivate"),
55
+ path.resolve(binDir, "bluefairy-deactivate.cmd"),
56
+ path.resolve(binDir, "bluefairy-deactivate.ps1"),
54
57
  ]
55
58
  }
56
59
  return [
57
60
  path.resolve(binDir, "bluefairy"),
58
61
  path.resolve(binDir, "bluefairy-activate"),
62
+ path.resolve(binDir, "bluefairy-deactivate"),
59
63
  ]
60
64
  }
61
65
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.1.72",
3
+ "version": "7.1.73",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -8268,6 +8268,67 @@ class Server {
8268
8268
  Math.random().toString(16).slice(2)
8269
8269
  ].join("-")
8270
8270
  }
8271
+ const prepareUniversalLauncherTarget = async ({ type, name, prompt, tool, uploadToken }) => {
8272
+ const normalizedType = typeof type === "string" ? type.trim().toLowerCase() : ""
8273
+ if (normalizedType !== "ask" && normalizedType !== "create_plugin") {
8274
+ const error = new Error("Unsupported launcher type.")
8275
+ error.status = 400
8276
+ throw error
8277
+ }
8278
+
8279
+ const pluginHref = resolveUniversalLauncherPluginHref(tool)
8280
+ let folderName = typeof name === "string" ? name.trim() : ""
8281
+ if (normalizedType === "ask" && !folderName) {
8282
+ folderName = await generateTerminalWorkspaceFolderName()
8283
+ }
8284
+ if (!folderName) {
8285
+ const error = new Error("Folder name is required.")
8286
+ error.status = 400
8287
+ throw error
8288
+ }
8289
+
8290
+ const rootDir = normalizedType === "ask"
8291
+ ? path.resolve(getTerminalWorkspacesRoot())
8292
+ : path.resolve(this.kernel.path("plugin"))
8293
+ const targetPath = await createLauncherTargetFolder(rootDir, folderName)
8294
+ try {
8295
+ await copyLauncherUploadsToDir(uploadToken, targetPath)
8296
+ } catch (error) {
8297
+ await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
8298
+ throw error
8299
+ }
8300
+ try {
8301
+ await bootstrapLauncherInstructionFiles(targetPath)
8302
+ } catch (error) {
8303
+ await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
8304
+ throw error
8305
+ }
8306
+ try {
8307
+ await persistLauncherPromptContext(targetPath, {
8308
+ prompt,
8309
+ includeSpec: true,
8310
+ includeRequest: normalizedType === "ask"
8311
+ })
8312
+ } catch (error) {
8313
+ await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
8314
+ throw error
8315
+ }
8316
+
8317
+ const params = new URLSearchParams()
8318
+ params.set("cwd", targetPath)
8319
+ params.set("chrome", "full")
8320
+ if (prompt) {
8321
+ params.set("prompt", prompt)
8322
+ }
8323
+
8324
+ return {
8325
+ ok: true,
8326
+ type: normalizedType,
8327
+ name: folderName,
8328
+ cwd: targetPath,
8329
+ url: `${pluginHref}?${params.toString()}`
8330
+ }
8331
+ }
8271
8332
  const prepareUniversalCreateApp = async ({ name, prompt, tool, uploadToken }) => {
8272
8333
  const folderName = typeof name === "string" ? name.trim() : ""
8273
8334
  if (!folderName) {
@@ -8345,6 +8406,44 @@ class Server {
8345
8406
  }
8346
8407
  return `${baseName}-${Date.now()}`
8347
8408
  }
8409
+ const suggestTaskWorkspaceName = async (task) => {
8410
+ const baseName = taskPackages.slugify(
8411
+ task && task.config ? task.config.title : (task && task.id ? task.id : "task"),
8412
+ "task"
8413
+ )
8414
+ let links = { workspaces: [] }
8415
+ try {
8416
+ links = await taskWorkspaceLinks.listTaskWorkspaces(task && task.id ? task.id : "", {
8417
+ root: "workspaces",
8418
+ pruneMissing: true
8419
+ })
8420
+ } catch (_) {
8421
+ }
8422
+ const existingNames = new Set(
8423
+ (Array.isArray(links.workspaces) ? links.workspaces : [])
8424
+ .map((workspace) => {
8425
+ const ref = workspace && typeof workspace.ref === "string" ? workspace.ref.trim() : ""
8426
+ if (!ref) {
8427
+ return ""
8428
+ }
8429
+ const relative = ref.split("/").slice(1).join("/")
8430
+ return relative ? path.posix.basename(relative).toLowerCase() : ""
8431
+ })
8432
+ .filter(Boolean)
8433
+ )
8434
+ let attempt = 0
8435
+ while (attempt < 1000) {
8436
+ const suffix = attempt === 0 ? "" : `-${attempt + 1}`
8437
+ const maxBaseLength = Math.max(1, 80 - suffix.length)
8438
+ const candidateBase = baseName.slice(0, maxBaseLength) || "task"
8439
+ const candidate = `${candidateBase}${suffix}`
8440
+ if (!existingNames.has(candidate.toLowerCase())) {
8441
+ return candidate
8442
+ }
8443
+ attempt += 1
8444
+ }
8445
+ return `${baseName}-${Date.now()}`
8446
+ }
8348
8447
  const getTaskLaunchTarget = (taskConfig) => {
8349
8448
  const validatedTaskConfig = taskPackages.validateTaskConfig(taskConfig)
8350
8449
  return validatedTaskConfig.target
@@ -8656,7 +8755,7 @@ class Server {
8656
8755
  const taskUi = buildTaskPresentationState(task, shareState)
8657
8756
  const sidebarContext = await buildTaskSidebarContext()
8658
8757
  const suggestedFolderName = task && task.config && usesWorkspaceTaskTarget(task.config)
8659
- ? ""
8758
+ ? await suggestTaskWorkspaceName(task)
8660
8759
  : taskPackages.slugify(task && task.config ? task.config.title : task.id, task && task.id ? task.id : "task")
8661
8760
  const renderedPrompt = taskPackages.applyTemplateValues(task.template, promptValues)
8662
8761
  const protocol = (req.$source && req.$source.protocol) || req.protocol || "http"
@@ -9943,37 +10042,84 @@ class Server {
9943
10042
  }
9944
10043
 
9945
10044
  const pluginHref = resolveUniversalLauncherPluginHref(selectedTool)
10045
+ const launchTarget = getTaskLaunchTarget(task.config)
9946
10046
  const launchRoot = getTaskLaunchRoot(task.config)
9947
10047
  let folderName = folderNameInput
9948
10048
  if (!folderName) {
9949
- if (usesWorkspaceTaskTarget(task.config)) {
9950
- folderName = await generateTerminalWorkspaceFolderName()
10049
+ if (launchTarget === "workspaces") {
10050
+ folderName = await suggestTaskWorkspaceName(task)
9951
10051
  } else {
9952
- folderName = await suggestTaskFolderName(launchRoot, task.config.title)
10052
+ await renderTaskLaunchPage(req, res, task, {
10053
+ selectedTool,
10054
+ inputValues,
10055
+ folderName: "",
10056
+ error: "Folder name is required."
10057
+ })
10058
+ return
9953
10059
  }
9954
10060
  }
9955
10061
 
9956
- let targetPath
9957
- try {
9958
- targetPath = await createLauncherTargetFolder(launchRoot, folderName)
9959
- } catch (error) {
10062
+ const prompt = taskPackages.applyTemplateValues(task.template, filterFilledTaskInputValues(inputValues)).trim()
10063
+ if (!prompt) {
9960
10064
  await renderTaskLaunchPage(req, res, task, {
9961
10065
  selectedTool,
9962
10066
  inputValues,
9963
10067
  folderName: folderNameInput || folderName,
9964
- error: error && error.message ? error.message : "Failed to create task folder."
10068
+ error: "The rendered task prompt is empty."
9965
10069
  })
9966
10070
  return
9967
10071
  }
9968
10072
 
9969
- const prompt = taskPackages.applyTemplateValues(task.template, filterFilledTaskInputValues(inputValues)).trim()
9970
- if (!prompt) {
9971
- await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
10073
+ if (launchTarget === "api") {
10074
+ try {
10075
+ const payload = await prepareUniversalCreateApp({
10076
+ name: folderName,
10077
+ prompt,
10078
+ tool: selectedTool,
10079
+ uploadToken: ""
10080
+ })
10081
+ res.redirect(payload.url)
10082
+ } catch (error) {
10083
+ await renderTaskLaunchPage(req, res, task, {
10084
+ selectedTool,
10085
+ inputValues,
10086
+ folderName: folderNameInput || folderName,
10087
+ error: error && error.message ? error.message : "Failed to create app."
10088
+ })
10089
+ }
10090
+ return
10091
+ }
10092
+
10093
+ if (launchTarget === "plugin") {
10094
+ try {
10095
+ const payload = await prepareUniversalLauncherTarget({
10096
+ type: "create_plugin",
10097
+ name: folderName,
10098
+ prompt,
10099
+ tool: selectedTool,
10100
+ uploadToken: ""
10101
+ })
10102
+ res.redirect(payload.url)
10103
+ } catch (error) {
10104
+ await renderTaskLaunchPage(req, res, task, {
10105
+ selectedTool,
10106
+ inputValues,
10107
+ folderName: folderNameInput || folderName,
10108
+ error: error && error.message ? error.message : "Failed to initialize task folder."
10109
+ })
10110
+ }
10111
+ return
10112
+ }
10113
+
10114
+ let targetPath
10115
+ try {
10116
+ targetPath = await createLauncherTargetFolder(launchRoot, folderName)
10117
+ } catch (error) {
9972
10118
  await renderTaskLaunchPage(req, res, task, {
9973
10119
  selectedTool,
9974
10120
  inputValues,
9975
10121
  folderName: folderNameInput || folderName,
9976
- error: "The rendered task prompt is empty."
10122
+ error: error && error.message ? error.message : "Failed to create task folder."
9977
10123
  })
9978
10124
  return
9979
10125
  }
@@ -10006,6 +10152,29 @@ class Server {
10006
10152
  })
10007
10153
  return
10008
10154
  }
10155
+ if (launchTarget === "workspaces") {
10156
+ const workspaceRef = taskWorkspaceLinks.createWorkspaceRef("workspaces", targetPath)
10157
+ if (!workspaceRef) {
10158
+ await renderTaskLaunchPage(req, res, task, {
10159
+ selectedTool,
10160
+ inputValues,
10161
+ folderName: folderNameInput || folderName,
10162
+ error: "Failed to remember the created workspace."
10163
+ })
10164
+ return
10165
+ }
10166
+ try {
10167
+ await taskWorkspaceLinks.touchTaskWorkspace(task.id, workspaceRef)
10168
+ } catch (error) {
10169
+ await renderTaskLaunchPage(req, res, task, {
10170
+ selectedTool,
10171
+ inputValues,
10172
+ folderName: folderNameInput || folderName,
10173
+ error: error && error.message ? error.message : "Failed to remember the created workspace."
10174
+ })
10175
+ return
10176
+ }
10177
+ }
10009
10178
 
10010
10179
  const params = new URLSearchParams()
10011
10180
  params.set("cwd", targetPath)
@@ -10438,9 +10607,7 @@ class Server {
10438
10607
  try {
10439
10608
  const body = req.body && typeof req.body === "object" ? req.body : {}
10440
10609
  const type = typeof body.type === "string" ? body.type.trim().toLowerCase() : ""
10441
- const requestedFolderName = typeof body.name === "string" ? body.name.trim() : ""
10442
10610
  const prompt = typeof body.prompt === "string" ? body.prompt.trim() : ""
10443
- const pluginHref = resolveUniversalLauncherPluginHref(body.tool)
10444
10611
 
10445
10612
  if (type !== "ask" && type !== "create_plugin") {
10446
10613
  res.status(400).json({
@@ -10449,59 +10616,14 @@ class Server {
10449
10616
  })
10450
10617
  return
10451
10618
  }
10452
- let folderName = requestedFolderName
10453
- if (type === "ask" && !folderName) {
10454
- folderName = await generateTerminalWorkspaceFolderName()
10455
- }
10456
- if (!folderName) {
10457
- res.status(400).json({
10458
- ok: false,
10459
- error: "Folder name is required."
10460
- })
10461
- return
10462
- }
10463
-
10464
- const rootDir = type === "ask"
10465
- ? path.resolve(getTerminalWorkspacesRoot())
10466
- : path.resolve(this.kernel.path("plugin"))
10467
- const targetPath = await createLauncherTargetFolder(rootDir, folderName)
10468
- try {
10469
- await copyLauncherUploadsToDir(body.uploadToken, targetPath)
10470
- } catch (error) {
10471
- await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
10472
- throw error
10473
- }
10474
- try {
10475
- await bootstrapLauncherInstructionFiles(targetPath)
10476
- } catch (error) {
10477
- await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
10478
- throw error
10479
- }
10480
- try {
10481
- await persistLauncherPromptContext(targetPath, {
10482
- prompt,
10483
- includeSpec: true,
10484
- includeRequest: type === "ask"
10485
- })
10486
- } catch (error) {
10487
- await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
10488
- throw error
10489
- }
10490
-
10491
- const params = new URLSearchParams()
10492
- params.set("cwd", targetPath)
10493
- params.set("chrome", "full")
10494
- if (prompt) {
10495
- params.set("prompt", prompt)
10496
- }
10497
-
10498
- res.json({
10499
- ok: true,
10619
+ const payload = await prepareUniversalLauncherTarget({
10500
10620
  type,
10501
- name: folderName,
10502
- cwd: targetPath,
10503
- url: `${pluginHref}?${params.toString()}`
10621
+ name: typeof body.name === "string" ? body.name : "",
10622
+ prompt,
10623
+ tool: typeof body.tool === "string" ? body.tool : "",
10624
+ uploadToken: typeof body.uploadToken === "string" ? body.uploadToken : ""
10504
10625
  })
10626
+ res.json(payload)
10505
10627
  } catch (error) {
10506
10628
  const status = Number.isInteger(error && error.status) ? error.status : 500
10507
10629
  res.status(status).json({
@@ -280,15 +280,25 @@ body.task-page .task-shell {
280
280
  }
281
281
 
282
282
  .task-detail-sidebar {
283
+ position: relative;
283
284
  min-width: 0;
284
285
  align-self: start;
285
286
  margin-top: 0;
286
- padding: 2px 0 0 28px;
287
- border-left: 1px solid var(--task-border);
287
+ padding: 2px 0 0 40px;
288
288
  border-radius: 0;
289
289
  background: transparent;
290
290
  }
291
291
 
292
+ .task-detail-sidebar::before {
293
+ content: "";
294
+ position: absolute;
295
+ top: 0;
296
+ bottom: 0;
297
+ left: 12px;
298
+ width: 1px;
299
+ background: var(--task-border);
300
+ }
301
+
292
302
  .task-shell {
293
303
  width: min(880px, calc(100vw - 32px));
294
304
  margin: 24px auto 40px;
@@ -2758,10 +2768,13 @@ body.dark .plugin-option .option-icon {
2758
2768
  .task-detail-sidebar {
2759
2769
  margin-top: 0;
2760
2770
  padding: 18px 0 0;
2761
- border-left: 0;
2762
2771
  border-top: 1px solid var(--task-border);
2763
2772
  }
2764
2773
 
2774
+ .task-detail-sidebar::before {
2775
+ display: none;
2776
+ }
2777
+
2765
2778
  .task-shell {
2766
2779
  width: min(100vw - 20px, 100%);
2767
2780
  margin: 12px auto 20px;
@@ -24,6 +24,10 @@
24
24
  ? 'Creates a new plugin.'
25
25
  : 'Runs in a new workspace.'
26
26
  const folderMeta = effectiveTaskTarget === 'workspaces' ? 'Optional' : ''
27
+ const folderRequired = effectiveTaskTarget !== 'workspaces'
28
+ const folderPlaceholder = effectiveTaskTarget === 'workspaces'
29
+ ? (suggestedFolderName || 'auto-generated')
30
+ : 'Enter folder name, e.g. my-project'
27
31
  const headerDescription = task.config.description && !/{{/.test(task.config.description)
28
32
  ? task.config.description
29
33
  : ''
@@ -157,8 +161,8 @@
157
161
  </label>
158
162
 
159
163
  <label class="task-field">
160
- <span class="task-label">Folder<% if (folderMeta) { %><span class="task-label-meta"><%= folderMeta %></span><% } %></span>
161
- <input class="task-input" type="text" name="folderName" value="<%= folderName || '' %>" placeholder="<%= suggestedFolderName || 'auto-generated' %>">
164
+ <span class="task-label">Folder<% if (folderRequired) { %><span class="task-label-required">*</span><% } %><% if (folderMeta) { %><span class="task-label-meta"><%= folderMeta %></span><% } %></span>
165
+ <input class="task-input" type="text" name="folderName" value="<%= folderName || '' %>" placeholder="<%= folderPlaceholder %>" <%= folderRequired ? 'required' : '' %>>
162
166
  </label>
163
167
  </div>
164
168
  </section>