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.
- package/kernel/bin/bluefairy.js +5 -1
- package/package.json +1 -1
- package/server/index.js +188 -66
- package/server/public/task-launcher.css +16 -3
- package/server/views/task_launch.ejs +6 -2
package/kernel/bin/bluefairy.js
CHANGED
|
@@ -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.
|
|
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
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 (
|
|
9950
|
-
folderName = await
|
|
10049
|
+
if (launchTarget === "workspaces") {
|
|
10050
|
+
folderName = await suggestTaskWorkspaceName(task)
|
|
9951
10051
|
} else {
|
|
9952
|
-
|
|
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
|
-
|
|
9957
|
-
|
|
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:
|
|
10068
|
+
error: "The rendered task prompt is empty."
|
|
9965
10069
|
})
|
|
9966
10070
|
return
|
|
9967
10071
|
}
|
|
9968
10072
|
|
|
9969
|
-
|
|
9970
|
-
|
|
9971
|
-
|
|
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: "
|
|
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
|
-
|
|
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:
|
|
10502
|
-
|
|
10503
|
-
|
|
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
|
|
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="<%=
|
|
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>
|