pinokiod 7.2.16 → 7.2.18

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 (51) hide show
  1. package/kernel/agent_instructions.js +166 -0
  2. package/kernel/api/index.js +137 -12
  3. package/kernel/bin/huggingface.js +1 -1
  4. package/kernel/environment.js +23 -9
  5. package/kernel/plugin_sources.js +57 -4
  6. package/kernel/prototype.js +4 -0
  7. package/kernel/shell.js +2 -0
  8. package/kernel/watch/index.js +31 -4
  9. package/package.json +1 -1
  10. package/server/features/index.js +4 -4
  11. package/server/features/{drafts → notes}/index.js +9 -9
  12. package/server/features/{drafts → notes}/parser.js +12 -7
  13. package/server/features/notes/public/notes.css +955 -0
  14. package/server/features/notes/public/notes.js +1149 -0
  15. package/server/features/{drafts → notes}/registry_import.js +59 -74
  16. package/server/features/notes/routes.js +156 -0
  17. package/server/features/notes/service.js +326 -0
  18. package/server/features/{drafts → notes}/watcher.js +14 -16
  19. package/server/index.js +61 -30
  20. package/server/lib/content_validation.js +19 -8
  21. package/server/lib/workspace_catalog.js +18 -18
  22. package/server/public/task-launcher.css +11 -3
  23. package/server/public/tasker.css +336 -0
  24. package/server/public/tasker.js +407 -0
  25. package/server/views/d.ejs +33 -2
  26. package/server/views/partials/menu.ejs +1 -1
  27. package/server/views/partials/workspace_row.ejs +11 -11
  28. package/server/views/pre.ejs +1 -1
  29. package/server/views/task_launch.ejs +10 -10
  30. package/server/views/tasker.ejs +40 -0
  31. package/server/views/terminal.ejs +15 -6
  32. package/server/views/terminals.ejs +0 -1
  33. package/server/views/workspaces.ejs +2 -1
  34. package/system/plugin/antigravity/pinokio.js +2 -4
  35. package/system/plugin/claude/pinokio.js +2 -4
  36. package/system/plugin/claude-auto/pinokio.js +2 -4
  37. package/system/plugin/claude-desktop/pinokio.js +2 -4
  38. package/system/plugin/codex/pinokio.js +2 -4
  39. package/system/plugin/codex-auto/pinokio.js +2 -4
  40. package/system/plugin/codex-desktop/pinokio.js +2 -4
  41. package/system/plugin/crush/pinokio.js +2 -4
  42. package/system/plugin/cursor/pinokio.js +2 -4
  43. package/system/plugin/gemini/pinokio.js +2 -4
  44. package/system/plugin/gemini-auto/pinokio.js +2 -4
  45. package/system/plugin/qwen/pinokio.js +2 -4
  46. package/system/plugin/vscode/pinokio.js +2 -4
  47. package/system/plugin/windsurf/pinokio.js +2 -4
  48. package/test/plugin-sources.test.js +45 -0
  49. package/server/features/drafts/public/drafts.js +0 -1569
  50. package/server/features/drafts/routes.js +0 -68
  51. package/server/features/drafts/service.js +0 -261
@@ -0,0 +1,166 @@
1
+ const fs = require("fs")
2
+ const path = require("path")
3
+ const PluginSources = require("./plugin_sources")
4
+
5
+ const NOTES_BEGIN = "<!-- PINOKIO:NOTES:BEGIN -->"
6
+ const NOTES_END = "<!-- PINOKIO:NOTES:END -->"
7
+ const LEGACY_DRAFTS_BEGIN = "<!-- PINOKIO:DRAFTS:BEGIN -->"
8
+ const LEGACY_DRAFTS_END = "<!-- PINOKIO:DRAFTS:END -->"
9
+
10
+ const AGENT_INSTRUCTION_FILES = [
11
+ "AGENTS.md",
12
+ "CLAUDE.md",
13
+ "GEMINI.md",
14
+ "QWEN.md",
15
+ ".windsurfrules",
16
+ ".cursorrules",
17
+ ".clinerules",
18
+ ]
19
+
20
+ const escapeRegExp = (value) => String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
21
+
22
+ const normalizeBlock = (block) => `${String(block || "").trim()}\n`
23
+
24
+ const extractManagedBlock = (content, begin = NOTES_BEGIN, end = NOTES_END) => {
25
+ const text = String(content || "")
26
+ const start = text.indexOf(begin)
27
+ const finish = text.indexOf(end, start + begin.length)
28
+ if (start < 0 || finish < 0) {
29
+ return ""
30
+ }
31
+ return text.slice(start, finish + end.length)
32
+ }
33
+
34
+ const insertionIndex = (content) => {
35
+ const text = String(content || "")
36
+ let offset = 0
37
+
38
+ if (text.startsWith("---\n")) {
39
+ const frontmatterEnd = text.indexOf("\n---\n", 4)
40
+ if (frontmatterEnd >= 0) {
41
+ offset = frontmatterEnd + "\n---\n".length
42
+ while (text[offset] === "\n") {
43
+ offset += 1
44
+ }
45
+ }
46
+ }
47
+
48
+ const rest = text.slice(offset)
49
+ const h1 = rest.match(/^# .*(?:\n|$)/)
50
+ if (h1) {
51
+ return offset + h1[0].length
52
+ }
53
+
54
+ return offset
55
+ }
56
+
57
+ const insertManagedBlock = (content, block) => {
58
+ const text = String(content || "").replace(/\r\n/g, "\n")
59
+ const noteBlock = normalizeBlock(block)
60
+ if (!text.trim()) {
61
+ return noteBlock
62
+ }
63
+
64
+ const index = insertionIndex(text)
65
+ const before = text.slice(0, index)
66
+ const after = text.slice(index).replace(/^\n+/, "")
67
+ const beforeGap = before.endsWith("\n\n") ? "" : before.endsWith("\n") ? "\n" : "\n\n"
68
+ const afterGap = after ? "\n" : ""
69
+
70
+ return `${before}${beforeGap}${noteBlock}${afterGap}${after}`
71
+ }
72
+
73
+ const upsertManagedBlock = (content, block, begin = NOTES_BEGIN, end = NOTES_END) => {
74
+ let text = String(content || "").replace(/\r\n/g, "\n")
75
+ const noteBlock = normalizeBlock(block)
76
+ const pattern = new RegExp(`${escapeRegExp(begin)}[\\s\\S]*?${escapeRegExp(end)}\\n?`)
77
+ const legacyPattern = new RegExp(`${escapeRegExp(LEGACY_DRAFTS_BEGIN)}[\\s\\S]*?${escapeRegExp(LEGACY_DRAFTS_END)}\\n?`)
78
+
79
+ if (pattern.test(text)) {
80
+ return text.replace(pattern, `${noteBlock}\n`)
81
+ }
82
+
83
+ text = text.replace(legacyPattern, "")
84
+ return insertManagedBlock(text, noteBlock)
85
+ }
86
+
87
+ const containedBy = (child, parent) => {
88
+ const relative = path.relative(path.resolve(parent), path.resolve(child))
89
+ return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative))
90
+ }
91
+
92
+ const isPluginScriptPath = (kernel, scriptPath) => {
93
+ if (!kernel || !scriptPath) {
94
+ return false
95
+ }
96
+
97
+ const absolutePath = path.resolve(scriptPath)
98
+ if (containedBy(absolutePath, kernel.path("plugin", "code"))) {
99
+ return false
100
+ }
101
+
102
+ const roots = [
103
+ PluginSources.systemPluginRoot(kernel),
104
+ kernel.path("plugin"),
105
+ ].filter(Boolean)
106
+
107
+ return roots.some((root) => containedBy(absolutePath, root))
108
+ }
109
+
110
+ const ensureManagedBlockInFile = async (filePath, block) => {
111
+ let content = ""
112
+ try {
113
+ content = await fs.promises.readFile(filePath, "utf8")
114
+ } catch (e) {
115
+ if (!e || e.code !== "ENOENT") {
116
+ throw e
117
+ }
118
+ }
119
+
120
+ const nextContent = upsertManagedBlock(content, block)
121
+ if (nextContent !== content) {
122
+ await fs.promises.writeFile(filePath, nextContent, "utf8")
123
+ return true
124
+ }
125
+
126
+ return false
127
+ }
128
+
129
+ const ensureNoteInstructionsForCwd = async ({ kernel, cwd }) => {
130
+ if (!kernel || !cwd) {
131
+ return { updated: [], skipped: "missing-cwd" }
132
+ }
133
+
134
+ const targetDir = path.resolve(cwd)
135
+ const stat = await fs.promises.stat(targetDir).catch(() => null)
136
+ if (!stat || !stat.isDirectory()) {
137
+ return { updated: [], skipped: "invalid-cwd" }
138
+ }
139
+
140
+ const sourcePath = kernel.path("prototype", "system", "AGENTS.md")
141
+ const source = await fs.promises.readFile(sourcePath, "utf8").catch(() => "")
142
+ const block = extractManagedBlock(source)
143
+ if (!block) {
144
+ return { updated: [], skipped: "missing-block" }
145
+ }
146
+
147
+ const updated = []
148
+ for (const filename of AGENT_INSTRUCTION_FILES) {
149
+ const filePath = path.join(targetDir, filename)
150
+ if (await ensureManagedBlockInFile(filePath, block)) {
151
+ updated.push(filePath)
152
+ }
153
+ }
154
+
155
+ return { updated, skipped: null }
156
+ }
157
+
158
+ module.exports = {
159
+ NOTES_BEGIN,
160
+ NOTES_END,
161
+ AGENT_INSTRUCTION_FILES,
162
+ extractManagedBlock,
163
+ upsertManagedBlock,
164
+ isPluginScriptPath,
165
+ ensureNoteInstructionsForCwd,
166
+ }
@@ -12,6 +12,7 @@ const Loader = require("../loader")
12
12
  const Environment = require("../environment")
13
13
  const Util = require('../util')
14
14
  const ShellRunTemplate = require('./shell_run_template')
15
+ const AgentInstructions = require("../agent_instructions")
15
16
 
16
17
  class Api {
17
18
  constructor(kernel) {
@@ -27,6 +28,7 @@ class Api {
27
28
  this.proxies = {}
28
29
  this.mods = {}
29
30
  this.child_procs = {}
31
+ this.resolved_actions = {}
30
32
  this.lproxy = new Lproxy()
31
33
  }
32
34
  startData(rpc) {
@@ -233,7 +235,7 @@ class Api {
233
235
  meta.ui = `/p/${api_name}`
234
236
  meta.browse = `/p/${api_name}/dev`
235
237
  } else {
236
- meta.icon = meta.icon ? `/asset/api/${api_name}/${meta.icon}` : "/pinokio-black.png"
238
+ meta.icon = meta.icon ? `/asset/api/${api_name}/${relpath}/${meta.icon}` : "/pinokio-black.png"
237
239
  meta.link = `/p/${api_name}/${relpath}/dev#n1`
238
240
  meta.web_path = `/api/${api_name}/${relpath}`
239
241
  meta.ui = `/p/${api_name}/${relpath}`
@@ -412,6 +414,84 @@ class Api {
412
414
  let result = await endpoint(rpc, ondata, this.kernel)
413
415
  return result
414
416
  }
417
+ requestId(request) {
418
+ return request ? (request.id || request.path) : null
419
+ }
420
+ clearResolvedAction(requestOrId) {
421
+ const id = typeof requestOrId === "string" ? requestOrId : this.requestId(requestOrId)
422
+ if (id) {
423
+ delete this.resolved_actions[id]
424
+ }
425
+ }
426
+ setRunning(request, done) {
427
+ const id = this.requestId(request)
428
+ if (!id) {
429
+ return
430
+ }
431
+ this.running[id] = true
432
+ this.done[id] = done
433
+ }
434
+ isActionCandidate(action) {
435
+ return (Array.isArray(action) && action.length > 0) || typeof action === "function"
436
+ }
437
+ async actionContext({ request, script, scriptDir, actionKey, input, args }) {
438
+ const cwd = request.cwd || scriptDir
439
+ const id = this.requestId(request)
440
+ const env = await Environment.get2(request.path, this.kernel)
441
+ this.kernel.template.update({ envs: env, env })
442
+ const port = await this.kernel.port()
443
+ const name = path.relative(this.kernel.path("api"), scriptDir)
444
+ return {
445
+ kernel: this.kernel,
446
+ info: this.kernel.info,
447
+ script: this.kernel.script,
448
+ action: actionKey,
449
+ input,
450
+ args,
451
+ global: (this.kernel.memory.global[id] || {}),
452
+ local: (this.kernel.memory.local[id] || {}),
453
+ key: this.kernel.memory.key,
454
+ uri: request.uri,
455
+ cwd,
456
+ dirname: scriptDir,
457
+ exists: (...args) => {
458
+ return fs.existsSync(path.resolve(cwd, ...args))
459
+ },
460
+ running: (...args) => {
461
+ let fullpath = path.resolve(cwd, ...args)
462
+ return this.running[fullpath]
463
+ },
464
+ name,
465
+ self: script,
466
+ port,
467
+ env,
468
+ envs: env,
469
+ ...this.kernel.vars,
470
+ }
471
+ }
472
+ async resolveActionSteps({ request, script, scriptDir, actionKey, input, args }) {
473
+ const id = this.requestId(request)
474
+ const cached = id ? this.resolved_actions[id] : null
475
+ if (cached && cached.actionKey === actionKey && Array.isArray(cached.steps)) {
476
+ return cached.steps
477
+ }
478
+
479
+ const action = script ? script[actionKey] : null
480
+ let steps
481
+ if (Array.isArray(action)) {
482
+ steps = action
483
+ } else if (typeof action === "function") {
484
+ const context = await this.actionContext({ request, script, scriptDir, actionKey, input, args })
485
+ steps = await action.call(script, this.kernel, this.kernel.info, context)
486
+ } else {
487
+ steps = null
488
+ }
489
+
490
+ if (id && Array.isArray(steps)) {
491
+ this.resolved_actions[id] = { actionKey, steps }
492
+ }
493
+ return steps
494
+ }
415
495
  async stop(req, ondata) {
416
496
  // 1. set the "stop" flag for the uri, so the next execution in the queue for the uri will NOT queue another task
417
497
  // 2. stream a message closing the socket
@@ -420,10 +500,12 @@ class Api {
420
500
  // /Users/x/pinokio/prototype/system/aicode/template/claude.json?cwd=/Users/x/pinokio/api/MMAudio.git
421
501
  // take the first part only since the rest is the cwd
422
502
  req.params.uri = req.params.id.split("?")[0]
503
+ this.clearResolvedAction(req.params.id)
423
504
  }
424
505
 
425
506
  // 1. if the scropt has 'on.stop', run it when stopping
426
507
  let requestPath = this.filePath(req.params.uri)
508
+ this.clearResolvedAction(requestPath)
427
509
  let { cwd, script } = await this.resolveScript(requestPath)
428
510
  if (script.on) {
429
511
  if (script.on.stop) {
@@ -910,7 +992,7 @@ class Api {
910
992
 
911
993
  let { cwd: scriptDir, script } = await this.resolveScript(request.path)
912
994
  const actionKey = request.action || 'run'
913
- const steps = (script && Array.isArray(script[actionKey])) ? script[actionKey] : []
995
+ const steps = await this.resolveActionSteps({ request, script, scriptDir, actionKey, input, args }) || []
914
996
  const totalSteps = steps.length
915
997
 
916
998
  let name = path.relative(this.kernel.path("api"), scriptDir)
@@ -1494,6 +1576,22 @@ class Api {
1494
1576
  args: input
1495
1577
  })
1496
1578
  }
1579
+ async ensurePluginAgentInstructions(request) {
1580
+ if (!request || !request.cwd || !request.path) {
1581
+ return
1582
+ }
1583
+ if (!AgentInstructions.isPluginScriptPath(this.kernel, request.path)) {
1584
+ return
1585
+ }
1586
+ try {
1587
+ await AgentInstructions.ensureNoteInstructionsForCwd({
1588
+ kernel: this.kernel,
1589
+ cwd: request.cwd,
1590
+ })
1591
+ } catch (e) {
1592
+ console.warn("[agent-instructions] failed to update note instructions:", e && e.message ? e.message : e)
1593
+ }
1594
+ }
1497
1595
  createQueue(queue_id, concurrency) {
1498
1596
  this.queues[queue_id] = fastq.promise(async ({ request, rawrpc, input, step, total, cwd, args }) => {
1499
1597
  try {
@@ -1711,18 +1809,12 @@ class Api {
1711
1809
  } else {
1712
1810
  const actionKey = request.action || 'run'
1713
1811
  request.action = actionKey
1714
- const steps = script ? script[actionKey] : null
1812
+ const action = script ? script[actionKey] : null
1715
1813
 
1716
- // 3. Check if the resolved endpoint has the requested action attribute and it's an array
1717
- if (Array.isArray(steps) && steps.length > 0) {
1814
+ // 3. Check if the resolved endpoint has the requested action attribute and resolve it to steps.
1815
+ if (this.isActionCandidate(action)) {
1718
1816
 
1719
- if (request.id) {
1720
- this.running[request.id] = true
1721
- this.done[request.id] = done
1722
- } else if (request.path) {
1723
- this.running[request.path] = true
1724
- this.done[request.path] = done
1725
- }
1817
+ this.setRunning(request, done)
1726
1818
 
1727
1819
  // set DNS
1728
1820
 
@@ -1735,6 +1827,39 @@ class Api {
1735
1827
  }
1736
1828
 
1737
1829
  const initialPayload = typeof request.input === "undefined" ? {} : request.input
1830
+ let steps
1831
+ try {
1832
+ steps = await this.resolveActionSteps({
1833
+ request,
1834
+ script,
1835
+ scriptDir: cwd,
1836
+ actionKey,
1837
+ input: initialPayload,
1838
+ args: initialPayload
1839
+ })
1840
+ } catch (e) {
1841
+ this.clearResolvedAction(request)
1842
+ delete this.running[this.requestId(request)]
1843
+ this.ondata({
1844
+ id: request.id || request.path,
1845
+ type: "error",
1846
+ data: e.stack,
1847
+ })
1848
+ return
1849
+ }
1850
+
1851
+ if (!Array.isArray(steps) || steps.length === 0) {
1852
+ this.clearResolvedAction(request)
1853
+ delete this.running[this.requestId(request)]
1854
+ this.ondata({
1855
+ id: request.id || request.path,
1856
+ type: "error",
1857
+ data: `missing or invalid attribute: ${actionKey}`
1858
+ })
1859
+ return
1860
+ }
1861
+
1862
+ await this.ensurePluginAgentInstructions(request)
1738
1863
  await this.startWatchersForRequest(request, script, cwd, initialPayload)
1739
1864
  this.queue(request, steps[0], initialPayload, 0, steps.length, cwd, initialPayload)
1740
1865
 
@@ -18,7 +18,7 @@ class Huggingface {
18
18
  let version = this.kernel.bin.installed.conda_versions.huggingface_hub
19
19
  if (version) {
20
20
  let coerced = semver.coerce(version)
21
- if (semver.satisfies(coerced, ">=1.0.1")) {
21
+ if (coerced && semver.eq(coerced, "1.0.1")) {
22
22
  return true
23
23
  }
24
24
  }
@@ -154,6 +154,8 @@ const ensurePinokioCacheDirs = async (kernel, options = {}) => {
154
154
  logCachePreflight(`cache root mkdir failed path=${cacheRoot} ${formatCachePreflightError(error)}`)
155
155
  }
156
156
 
157
+ if (process.platform === "darwin") await fs.promises.writeFile(path.resolve(root, ".metadata_never_index"), "", { flag: "a" }).catch(() => {})
158
+
157
159
  const errors = []
158
160
  const results = []
159
161
 
@@ -618,18 +620,21 @@ const requirements = async (script, cwd, kernel) => {
618
620
  let requires_instantiation = false
619
621
  if (script) {
620
622
  let pre
621
- if (script.pre) {
623
+ if (Array.isArray(script.pre)) {
622
624
  pre = script.pre
623
- } else if (script.env) {
625
+ } else if (Array.isArray(script.env)) {
624
626
  pre = script.env
625
627
  }
626
628
  if (pre) {
627
629
  let env = await get2(cwd, kernel)
628
630
  for(let item of pre) {
631
+ if (!item || typeof item !== "object") {
632
+ continue
633
+ }
629
634
  let env_key
630
- if (item.env) {
635
+ if (typeof item.env === "string" && item.env) {
631
636
  env_key = item.env
632
- } else if (item.key) {
637
+ } else if (typeof item.key === "string" && item.key) {
633
638
  env_key = item.key
634
639
  item.env = item.key
635
640
  }
@@ -638,11 +643,20 @@ const requirements = async (script, cwd, kernel) => {
638
643
  if (!item.index) {
639
644
  item.index = 0
640
645
  }
641
- if (item.host) {
642
- const hasProtocol = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(item.host);
643
- const url = new URL(hasProtocol ? item.host : `https://${item.host}`);
644
- item.host = url.host
645
- item.val = await kernel.kv.get(item.host, item.index)
646
+ if (typeof item.host === "string" && item.host.trim()) {
647
+ const host = item.host.trim()
648
+ let parsedHost = ""
649
+ try {
650
+ const hasProtocol = /^[a-zA-Z][a-zA-Z\d+\-.]*:\/\//.test(host);
651
+ const url = new URL(hasProtocol ? host : `https://${host}`);
652
+ parsedHost = url.host
653
+ } catch (e) {
654
+ item.host = ""
655
+ }
656
+ if (parsedHost) {
657
+ item.host = parsedHost
658
+ item.val = await kernel.kv.get(item.host, item.index)
659
+ }
646
660
  } else {
647
661
  item.host = ""
648
662
  }
@@ -10,6 +10,7 @@ const LOCAL_PLUGIN_RUN_PREFIX = `${LOCAL_RUN_PREFIX}/plugin`
10
10
  const LOCAL_PLUGIN_ASSET_PREFIX = `${LOCAL_ASSET_PREFIX}/plugin`
11
11
  const SYSTEM_PLUGIN_RUN_PREFIX = `${SYSTEM_RUN_PREFIX}/plugin`
12
12
  const SYSTEM_PLUGIN_ASSET_PREFIX = `${SYSTEM_ASSET_PREFIX}/plugin`
13
+ const ACTION_KEYS = new Set(["run", "install", "uninstall", "update"])
13
14
 
14
15
  const toPathname = (value) => {
15
16
  const raw = typeof value === "string" ? value.trim() : ""
@@ -124,6 +125,29 @@ const pluginAssetHrefForIcon = (normalizedPath, icon) => {
124
125
  return ""
125
126
  }
126
127
 
128
+ const isAction = (value) => Array.isArray(value) || typeof value === "function"
129
+ const hasActionFunction = (config, key) => ACTION_KEYS.has(key) && typeof config[key] === "function"
130
+ const isPlainObject = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value)
131
+ const declaredPluginPath = (config) => typeof config.path === "string" ? config.path.trim() : ""
132
+ const hasInvalidTopLevelFunction = (config) => {
133
+ return Object.keys(config).some((key) => typeof config[key] === "function" && !hasActionFunction(config, key))
134
+ }
135
+ const hasInvalidAction = (config) => {
136
+ return Array.from(ACTION_KEYS).some((key) => key in config && !isAction(config[key]))
137
+ }
138
+ const isValidPluginConfig = (config, options = {}) => {
139
+ if (!isPlainObject(config) || !isAction(config.run)) {
140
+ return false
141
+ }
142
+ if (hasInvalidTopLevelFunction(config) || hasInvalidAction(config)) {
143
+ return false
144
+ }
145
+ if (options.standalone && declaredPluginPath(config) !== "plugin") {
146
+ return false
147
+ }
148
+ return true
149
+ }
150
+
127
151
  const normalizeLauncherTool = (toolValue) => {
128
152
  let normalizedTool = typeof toolValue === "string" ? toolValue.trim() : ""
129
153
  normalizedTool = normalizedTool.replace(/^https?:\/\/[^/]+/i, "")
@@ -161,7 +185,32 @@ const resolveLauncherPluginHref = (toolValue) => {
161
185
  return `${LOCAL_PLUGIN_RUN_PREFIX}/${normalizedTool}/pinokio.js`
162
186
  }
163
187
 
164
- const loadPluginsFromRoot = async ({ kernel, root, runPrefix, assetPrefix, source, ignore = [] }) => {
188
+ const resolveLauncherPluginSelection = (toolValue) => {
189
+ const href = resolveLauncherPluginHref(toolValue)
190
+ if (href.startsWith(`${LOCAL_RUN_PREFIX}/`)) {
191
+ return href.slice(LOCAL_RUN_PREFIX.length)
192
+ }
193
+ return href
194
+ }
195
+
196
+ const normalizeLauncherSuccessPlugin = (successUrl, toolValue) => {
197
+ if (typeof successUrl !== "string" || typeof toolValue !== "string" || !toolValue.trim()) {
198
+ return successUrl
199
+ }
200
+
201
+ try {
202
+ const parsed = new URL(successUrl, "http://localhost")
203
+ if (!parsed.searchParams.has("plugin")) {
204
+ return successUrl
205
+ }
206
+ parsed.searchParams.set("plugin", resolveLauncherPluginSelection(toolValue))
207
+ return `${parsed.pathname}${parsed.search}${parsed.hash}`
208
+ } catch (_) {
209
+ return successUrl
210
+ }
211
+ }
212
+
213
+ const loadPluginsFromRoot = async ({ kernel, root, runPrefix, assetPrefix, source, ignore = [], standalone = false }) => {
165
214
  const exists = await fs.promises.stat(root).then((stat) => stat.isDirectory()).catch(() => false)
166
215
  if (!exists) return []
167
216
 
@@ -170,9 +219,7 @@ const loadPluginsFromRoot = async ({ kernel, root, runPrefix, assetPrefix, sourc
170
219
  for (const pluginPath of pluginPaths) {
171
220
  const normalizedPluginPath = normalizeSlashes(pluginPath)
172
221
  const config = await kernel.require(path.resolve(root, pluginPath))
173
- if (!config || !Array.isArray(config.run)) continue
174
- const invalid = Object.keys(config).some((key) => typeof config[key] === "function")
175
- if (invalid) continue
222
+ if (!isValidPluginConfig(config, { standalone })) continue
176
223
 
177
224
  const cwd = normalizedPluginPath.split("/").slice(0, -1).join("/")
178
225
  const href = `${runPrefix}/${normalizedPluginPath}`
@@ -204,6 +251,7 @@ const loadPluginMenu = async (kernel) => {
204
251
  assetPrefix: LOCAL_PLUGIN_ASSET_PREFIX,
205
252
  source: "local",
206
253
  ignore: ["code/**"],
254
+ standalone: true,
207
255
  })
208
256
  return systemPlugins.concat(localPlugins)
209
257
  }
@@ -232,5 +280,10 @@ module.exports = {
232
280
  pluginRunHrefForPath,
233
281
  pluginAssetHrefForIcon,
234
282
  resolveLauncherPluginHref,
283
+ resolveLauncherPluginSelection,
284
+ normalizeLauncherSuccessPlugin,
235
285
  loadPluginMenu,
286
+ ACTION_KEYS,
287
+ isAction,
288
+ isValidPluginConfig,
236
289
  }
@@ -3,6 +3,7 @@ const path = require('path')
3
3
  const { glob, sync, hasMagic } = require('glob-gitignore')
4
4
  const marked = require('marked')
5
5
  const matter = require('gray-matter');
6
+ const PluginSources = require("./plugin_sources")
6
7
  class Proto {
7
8
  constructor(kernel) {
8
9
  this.kernel = kernel
@@ -148,6 +149,9 @@ class Proto {
148
149
 
149
150
 
150
151
  if (response) {
152
+ if (response.success) {
153
+ response.success = PluginSources.normalizeLauncherSuccessPlugin(response.success, req.params.tool)
154
+ }
151
155
  return response
152
156
  } else {
153
157
  return { success: "/p/" + name + "/dev" }
package/kernel/shell.js CHANGED
@@ -294,6 +294,8 @@ class Shell {
294
294
  }
295
295
  }
296
296
 
297
+ setDefaultEnvValue(this.env, "HF_HUB_DISABLE_UPDATE_CHECK", "1")
298
+
297
299
  if (this.platform === "win32") {
298
300
  // Hugging Face file symlinks regularly fail on non-admin Windows setups.
299
301
  // Default to no-symlink cache mode unless the user/app explicitly overrides it.
@@ -18,7 +18,34 @@ class WatchManager {
18
18
 
19
19
  hasHandler(script, handlerName) {
20
20
  const watches = script && Array.isArray(script.watch) ? script.watch : []
21
- return watches.some((watch) => watch && watch.handler === handlerName)
21
+ return watches.some((watch) => {
22
+ const resolved = this.resolveHandlerMethod(watch)
23
+ return resolved.handlerName === handlerName
24
+ })
25
+ }
26
+
27
+ resolveHandlerMethod(declaration) {
28
+ if (!declaration || typeof declaration !== "object") {
29
+ return { handlerName: "", methodName: "" }
30
+ }
31
+ const method = typeof declaration.method === "string" ? declaration.method.trim() : ""
32
+ if (declaration.handler) {
33
+ return {
34
+ handlerName: String(declaration.handler).trim(),
35
+ methodName: method
36
+ }
37
+ }
38
+ if (!declaration.uri && method.includes(".")) {
39
+ const parts = method.split(".")
40
+ return {
41
+ handlerName: parts.slice(0, -1).join(".").trim(),
42
+ methodName: parts[parts.length - 1].trim()
43
+ }
44
+ }
45
+ return {
46
+ handlerName: "",
47
+ methodName: method
48
+ }
22
49
  }
23
50
 
24
51
  renderDeclaration(raw, memory) {
@@ -105,10 +132,10 @@ class WatchManager {
105
132
  input,
106
133
  args
107
134
  })
108
- const methodName = typeof declaration.method === "string" ? declaration.method.trim() : ""
135
+ const { handlerName, methodName } = this.resolveHandlerMethod(declaration)
109
136
  let handler = null
110
- if (declaration.handler) {
111
- handler = this.handlers.get(String(declaration.handler).trim())
137
+ if (handlerName) {
138
+ handler = this.handlers.get(handlerName)
112
139
  } else if (declaration.uri) {
113
140
  handler = await this.resolveExternalHandler(ctx, declaration.uri)
114
141
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "7.2.16",
3
+ "version": "7.2.18",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
@@ -1,10 +1,10 @@
1
- const { createDraftFeature } = require("./drafts")
1
+ const { createNoteFeature } = require("./notes")
2
2
 
3
3
  async function mountFeatures(options = {}) {
4
- const drafts = createDraftFeature(options)
5
- await drafts.start()
4
+ const notes = createNoteFeature(options)
5
+ await notes.start()
6
6
  return {
7
- drafts
7
+ notes
8
8
  }
9
9
  }
10
10