pinokiod 7.2.18 → 7.3.0

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 (89) hide show
  1. package/Dockerfile +2 -0
  2. package/kernel/api/index.js +13 -179
  3. package/kernel/api/process/index.js +44 -99
  4. package/kernel/bin/conda-python.js +30 -0
  5. package/kernel/bin/conda.js +22 -3
  6. package/kernel/bin/huggingface.js +1 -1
  7. package/kernel/bin/index.js +11 -1
  8. package/kernel/environment.js +11 -205
  9. package/kernel/git.js +13 -0
  10. package/kernel/index.js +1 -64
  11. package/kernel/plugin.js +58 -6
  12. package/kernel/prototype.js +0 -4
  13. package/kernel/shell.js +2 -23
  14. package/kernel/util.js +0 -60
  15. package/package.json +1 -1
  16. package/server/index.js +171 -229
  17. package/server/lib/content_validation.js +33 -47
  18. package/server/public/common.js +29 -103
  19. package/server/public/create-launcher.js +31 -4
  20. package/server/public/electron.css +6 -0
  21. package/server/public/style.css +0 -337
  22. package/server/public/task-launcher.css +3 -11
  23. package/server/public/task-launcher.js +32 -5
  24. package/server/public/universal-launcher.js +26 -3
  25. package/server/socket.js +11 -22
  26. package/server/views/app.ejs +30 -167
  27. package/server/views/d.ejs +35 -33
  28. package/server/views/editor.ejs +4 -25
  29. package/server/views/partials/main_sidebar.ejs +0 -1
  30. package/server/views/partials/menu.ejs +1 -1
  31. package/server/views/pre.ejs +1 -1
  32. package/server/views/shell.ejs +3 -11
  33. package/server/views/task_launch.ejs +10 -10
  34. package/server/views/terminal.ejs +5 -34
  35. package/spec/INSTRUCTION_SYNC.md +5 -5
  36. package/kernel/agent_instructions.js +0 -166
  37. package/kernel/api/shell_run_template.js +0 -273
  38. package/kernel/api/uri/index.js +0 -51
  39. package/kernel/plugin_sources.js +0 -289
  40. package/kernel/watch/context.js +0 -42
  41. package/kernel/watch/drivers/fs.js +0 -71
  42. package/kernel/watch/drivers/poll.js +0 -33
  43. package/kernel/watch/index.js +0 -185
  44. package/server/features/index.js +0 -13
  45. package/server/features/notes/index.js +0 -41
  46. package/server/features/notes/parser.js +0 -174
  47. package/server/features/notes/public/notes.css +0 -955
  48. package/server/features/notes/public/notes.js +0 -1149
  49. package/server/features/notes/registry_import.js +0 -412
  50. package/server/features/notes/routes.js +0 -156
  51. package/server/features/notes/service.js +0 -326
  52. package/server/features/notes/watcher.js +0 -74
  53. package/server/lib/workspace_catalog.js +0 -151
  54. package/server/lib/workspace_runtime.js +0 -390
  55. package/server/public/tasker.css +0 -336
  56. package/server/public/tasker.js +0 -407
  57. package/server/routes/workspaces.js +0 -44
  58. package/server/views/partials/workspace_row.ejs +0 -61
  59. package/server/views/tasker.ejs +0 -40
  60. package/server/views/workspaces.ejs +0 -813
  61. package/system/plugin/antigravity/antigravity.png +0 -0
  62. package/system/plugin/antigravity/pinokio.js +0 -35
  63. package/system/plugin/claude/claude.png +0 -0
  64. package/system/plugin/claude/pinokio.js +0 -61
  65. package/system/plugin/claude-auto/claude.png +0 -0
  66. package/system/plugin/claude-auto/pinokio.js +0 -72
  67. package/system/plugin/claude-desktop/icon.jpeg +0 -0
  68. package/system/plugin/claude-desktop/pinokio.js +0 -37
  69. package/system/plugin/codex/openai.webp +0 -0
  70. package/system/plugin/codex/pinokio.js +0 -56
  71. package/system/plugin/codex-auto/openai.webp +0 -0
  72. package/system/plugin/codex-auto/pinokio.js +0 -63
  73. package/system/plugin/codex-desktop/icon.png +0 -0
  74. package/system/plugin/codex-desktop/pinokio.js +0 -37
  75. package/system/plugin/crush/crush.png +0 -0
  76. package/system/plugin/crush/pinokio.js +0 -29
  77. package/system/plugin/cursor/cursor.jpeg +0 -0
  78. package/system/plugin/cursor/pinokio.js +0 -37
  79. package/system/plugin/gemini/gemini.jpeg +0 -0
  80. package/system/plugin/gemini/pinokio.js +0 -38
  81. package/system/plugin/gemini-auto/gemini.jpeg +0 -0
  82. package/system/plugin/gemini-auto/pinokio.js +0 -41
  83. package/system/plugin/qwen/pinokio.js +0 -48
  84. package/system/plugin/qwen/qwen.png +0 -0
  85. package/system/plugin/vscode/pinokio.js +0 -34
  86. package/system/plugin/vscode/vscode.png +0 -0
  87. package/system/plugin/windsurf/pinokio.js +0 -37
  88. package/system/plugin/windsurf/windsurf.png +0 -0
  89. package/test/plugin-sources.test.js +0 -45
@@ -1,289 +0,0 @@
1
- const fs = require("fs")
2
- const path = require("path")
3
- const { glob } = require("glob")
4
-
5
- const LOCAL_RUN_PREFIX = "/run"
6
- const LOCAL_ASSET_PREFIX = "/asset"
7
- const SYSTEM_RUN_PREFIX = "/pinokio/run"
8
- const SYSTEM_ASSET_PREFIX = "/pinokio/asset"
9
- const LOCAL_PLUGIN_RUN_PREFIX = `${LOCAL_RUN_PREFIX}/plugin`
10
- const LOCAL_PLUGIN_ASSET_PREFIX = `${LOCAL_ASSET_PREFIX}/plugin`
11
- const SYSTEM_PLUGIN_RUN_PREFIX = `${SYSTEM_RUN_PREFIX}/plugin`
12
- const SYSTEM_PLUGIN_ASSET_PREFIX = `${SYSTEM_ASSET_PREFIX}/plugin`
13
- const ACTION_KEYS = new Set(["run", "install", "uninstall", "update"])
14
-
15
- const toPathname = (value) => {
16
- const raw = typeof value === "string" ? value.trim() : ""
17
- if (!raw) return ""
18
- try {
19
- if (/^https?:\/\//i.test(raw)) return new URL(raw).pathname
20
- if (raw.startsWith("/")) return new URL(`http://localhost${raw}`).pathname
21
- } catch (_) {
22
- }
23
- return raw.split("?")[0].split("#")[0]
24
- }
25
-
26
- const normalizeSlashes = (value) => String(value || "").replace(/\\/g, "/").replace(/\/{2,}/g, "/")
27
-
28
- const systemRoot = (kernel) => {
29
- if (kernel && typeof kernel.systemPath === "function") {
30
- return kernel.systemPath()
31
- }
32
- return path.resolve(__dirname, "..", "system")
33
- }
34
-
35
- const systemPluginRoot = (kernel) => {
36
- if (kernel && typeof kernel.systemPath === "function") {
37
- return kernel.systemPath("plugin")
38
- }
39
- return path.resolve(__dirname, "..", "system", "plugin")
40
- }
41
-
42
- const isSystemRunPath = (value) => toPathname(value).startsWith(`${SYSTEM_RUN_PREFIX}/`)
43
- const isLocalRunPath = (value) => toPathname(value).startsWith(`${LOCAL_RUN_PREFIX}/`)
44
- const isRunPath = (value) => isLocalRunPath(value) || isSystemRunPath(value)
45
- const isSystemPluginPath = (value) => normalizeSlashes(value).startsWith(`${SYSTEM_PLUGIN_RUN_PREFIX}/`)
46
- const isLegacyPluginCodePath = (value) => {
47
- const normalized = normalizeSlashes(value).replace(/^\/+/, "")
48
- return normalized.startsWith("plugin/code/") || normalized.startsWith("code/")
49
- }
50
-
51
- const normalizePluginPath = (value) => {
52
- let normalized = toPathname(value)
53
- if (!normalized) return ""
54
- normalized = normalizeSlashes(normalized)
55
- if (!normalized.startsWith(`${SYSTEM_RUN_PREFIX}/`)) {
56
- normalized = normalized.replace(/^\/run(?=\/)/, "")
57
- }
58
- if (!normalized.startsWith("/")) {
59
- normalized = `/${normalized}`
60
- }
61
- normalized = normalized.replace(/\/+$/, "")
62
- if (!normalized.endsWith("/pinokio.js")) {
63
- normalized = `${normalized}/pinokio.js`
64
- }
65
- return normalized
66
- }
67
-
68
- const pluginSelectionMatches = (src, selectedValue) => {
69
- const selectedPlugin = normalizeSlashes(typeof selectedValue === "string" ? selectedValue.trim() : "")
70
- if (!selectedPlugin || !src) return false
71
- const selectedRunPath = selectedPlugin.startsWith(`${SYSTEM_RUN_PREFIX}/`)
72
- ? selectedPlugin
73
- : `${LOCAL_RUN_PREFIX}${selectedPlugin}`
74
- return src === selectedPlugin || src.startsWith(selectedRunPath)
75
- }
76
-
77
- const resolveRunPath = (kernel, value) => {
78
- const pathname = toPathname(value)
79
- if (isSystemRunPath(pathname)) {
80
- const parts = pathname.split("/").filter(Boolean).slice(2)
81
- return kernel.systemPath(...parts)
82
- }
83
- if (isLocalRunPath(pathname)) {
84
- const parts = pathname.split("/").filter(Boolean).slice(1)
85
- return kernel.path(...parts)
86
- }
87
- return ""
88
- }
89
-
90
- const resolvePinokioPath = (kernel, value) => {
91
- const runPath = resolveRunPath(kernel, value)
92
- if (runPath) return runPath
93
- const pathname = toPathname(value)
94
- return pathname ? kernel.path(pathname.replace(/^\/+/, "")) : ""
95
- }
96
-
97
- const systemRelativeFromPluginPath = (normalizedPath) => {
98
- return normalizeSlashes(normalizedPath).replace(/^\/pinokio\/run\/+/, "")
99
- }
100
-
101
- const pluginPathToAbsolute = (kernel, normalizedPath) => {
102
- if (isSystemPluginPath(normalizedPath)) {
103
- return kernel.systemPath(systemRelativeFromPluginPath(normalizedPath))
104
- }
105
- return kernel.path(normalizeSlashes(normalizedPath).replace(/^\/+/, ""))
106
- }
107
-
108
- const pluginRunHrefForPath = (normalizedPath) => {
109
- if (typeof normalizedPath !== "string" || !normalizedPath) return ""
110
- if (isSystemPluginPath(normalizedPath)) return normalizedPath
111
- return `${LOCAL_RUN_PREFIX}${normalizedPath}`
112
- }
113
-
114
- const pluginAssetHrefForIcon = (normalizedPath, icon) => {
115
- const trimmedIcon = typeof icon === "string" ? icon.trim() : ""
116
- if (!trimmedIcon) return ""
117
- if (isSystemPluginPath(normalizedPath)) {
118
- const relativeDir = path.posix.dirname(systemRelativeFromPluginPath(normalizedPath))
119
- return `${SYSTEM_ASSET_PREFIX}/${relativeDir}/${trimmedIcon}`
120
- }
121
- if (normalizeSlashes(normalizedPath).startsWith("/plugin/")) {
122
- const relativeDir = path.posix.dirname(normalizeSlashes(normalizedPath).slice(1))
123
- return `${LOCAL_ASSET_PREFIX}/${relativeDir}/${trimmedIcon}`
124
- }
125
- return ""
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
-
151
- const normalizeLauncherTool = (toolValue) => {
152
- let normalizedTool = typeof toolValue === "string" ? toolValue.trim() : ""
153
- normalizedTool = normalizedTool.replace(/^https?:\/\/[^/]+/i, "")
154
- normalizedTool = normalizedTool.replace(/^\/+|\/+$/g, "")
155
- if (!normalizedTool || normalizedTool.includes("..") || !/^[A-Za-z0-9._/-]+$/.test(normalizedTool)) {
156
- const error = new Error("Invalid plugin.")
157
- error.status = 400
158
- throw error
159
- }
160
- if (!normalizedTool.startsWith("pinokio/run/")) {
161
- normalizedTool = normalizedTool.replace(/^run\//, "")
162
- }
163
- if (isLegacyPluginCodePath(normalizedTool)) {
164
- const error = new Error("The managed plugin/code path is no longer used.")
165
- error.status = 400
166
- throw error
167
- }
168
- return normalizedTool
169
- }
170
-
171
- const resolveLauncherPluginHref = (toolValue) => {
172
- const normalizedTool = normalizeLauncherTool(toolValue)
173
- if (normalizedTool.startsWith("pinokio/run/")) {
174
- const scriptPath = normalizedTool.endsWith(".js")
175
- ? normalizedTool
176
- : `${normalizedTool}/pinokio.js`
177
- return `/${scriptPath}`
178
- }
179
- if (normalizedTool.startsWith("plugin/") || normalizedTool.startsWith("api/")) {
180
- const scriptPath = normalizedTool.endsWith(".js")
181
- ? normalizedTool
182
- : `${normalizedTool}/pinokio.js`
183
- return `${LOCAL_RUN_PREFIX}/${scriptPath}`
184
- }
185
- return `${LOCAL_PLUGIN_RUN_PREFIX}/${normalizedTool}/pinokio.js`
186
- }
187
-
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 }) => {
214
- const exists = await fs.promises.stat(root).then((stat) => stat.isDirectory()).catch(() => false)
215
- if (!exists) return []
216
-
217
- const pluginPaths = await glob("**/pinokio.js", { cwd: root, ignore })
218
- const plugins = []
219
- for (const pluginPath of pluginPaths) {
220
- const normalizedPluginPath = normalizeSlashes(pluginPath)
221
- const config = await kernel.require(path.resolve(root, pluginPath))
222
- if (!isValidPluginConfig(config, { standalone })) continue
223
-
224
- const cwd = normalizedPluginPath.split("/").slice(0, -1).join("/")
225
- const href = `${runPrefix}/${normalizedPluginPath}`
226
- const image = config.icon ? `${assetPrefix}/${cwd}/${config.icon}` : config.image
227
- plugins.push({
228
- ...config,
229
- href,
230
- src: href,
231
- image,
232
- source,
233
- system: source === "system",
234
- })
235
- }
236
- return plugins
237
- }
238
-
239
- const loadPluginMenu = async (kernel) => {
240
- const systemPlugins = await loadPluginsFromRoot({
241
- kernel,
242
- root: systemPluginRoot(kernel),
243
- runPrefix: SYSTEM_PLUGIN_RUN_PREFIX,
244
- assetPrefix: SYSTEM_PLUGIN_ASSET_PREFIX,
245
- source: "system",
246
- })
247
- const localPlugins = await loadPluginsFromRoot({
248
- kernel,
249
- root: path.resolve(kernel.homedir, "plugin"),
250
- runPrefix: LOCAL_PLUGIN_RUN_PREFIX,
251
- assetPrefix: LOCAL_PLUGIN_ASSET_PREFIX,
252
- source: "local",
253
- ignore: ["code/**"],
254
- standalone: true,
255
- })
256
- return systemPlugins.concat(localPlugins)
257
- }
258
-
259
- module.exports = {
260
- LOCAL_RUN_PREFIX,
261
- LOCAL_ASSET_PREFIX,
262
- SYSTEM_RUN_PREFIX,
263
- SYSTEM_ASSET_PREFIX,
264
- LOCAL_PLUGIN_RUN_PREFIX,
265
- SYSTEM_PLUGIN_RUN_PREFIX,
266
- SYSTEM_PLUGIN_ASSET_PREFIX,
267
- systemRoot,
268
- systemPluginRoot,
269
- toPathname,
270
- isSystemRunPath,
271
- isLocalRunPath,
272
- isRunPath,
273
- isSystemPluginPath,
274
- isLegacyPluginCodePath,
275
- normalizePluginPath,
276
- pluginSelectionMatches,
277
- resolveRunPath,
278
- resolvePinokioPath,
279
- pluginPathToAbsolute,
280
- pluginRunHrefForPath,
281
- pluginAssetHrefForIcon,
282
- resolveLauncherPluginHref,
283
- resolveLauncherPluginSelection,
284
- normalizeLauncherSuccessPlugin,
285
- loadPluginMenu,
286
- ACTION_KEYS,
287
- isAction,
288
- isValidPluginConfig,
289
- }
@@ -1,42 +0,0 @@
1
- const path = require("path")
2
- const { watchFs } = require("./drivers/fs")
3
- const { poll } = require("./drivers/poll")
4
-
5
- class WatchContext {
6
- constructor(options = {}) {
7
- this.kernel = options.kernel
8
- this.manager = options.manager
9
- this.id = options.id
10
- this.cwd = path.resolve(options.cwd)
11
- this.dirname = path.resolve(options.dirname || options.cwd)
12
- this.request = options.request
13
- this.script = options.script
14
- this.declaration = options.declaration
15
- this.input = options.input || {}
16
- this.args = options.args || this.input
17
- this.watch = {
18
- fs: (targetPath, callback, watchOptions = {}) => {
19
- return watchFs(this.resolve(targetPath), callback, {
20
- ...watchOptions,
21
- onError: watchOptions.onError || ((error) => {
22
- console.warn("[watch.fs]", error && error.message ? error.message : error)
23
- })
24
- })
25
- }
26
- }
27
- }
28
-
29
- resolve(targetPath) {
30
- return this.kernel.api.resolvePath(this.cwd, String(targetPath || "."))
31
- }
32
-
33
- resolveModule(targetPath) {
34
- return this.kernel.api.resolvePath(this.dirname, String(targetPath || "."))
35
- }
36
-
37
- poll(interval, callback, options = {}) {
38
- return poll(interval, callback, options)
39
- }
40
- }
41
-
42
- module.exports = WatchContext
@@ -1,71 +0,0 @@
1
- const fs = require("fs")
2
- const path = require("path")
3
- const ParcelWatcher = require("@parcel/watcher")
4
-
5
- const DEFAULT_IGNORE = [
6
- "**/.git/**",
7
- "**/node_modules/**",
8
- "**/__pycache__/**",
9
- "**/.venv/**",
10
- "**/venv/**",
11
- "**/env/**"
12
- ]
13
-
14
- function isInside(candidate, parent) {
15
- const relative = path.relative(parent, candidate)
16
- return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative))
17
- }
18
-
19
- async function nearestExistingDirectory(targetPath) {
20
- let current = path.resolve(targetPath)
21
- while (current && current !== path.dirname(current)) {
22
- const stats = await fs.promises.stat(current).catch(() => null)
23
- if (stats && stats.isDirectory()) {
24
- return current
25
- }
26
- current = path.dirname(current)
27
- }
28
- return current || path.parse(path.resolve(targetPath)).root
29
- }
30
-
31
- async function watchFs(targetPath, callback, options = {}) {
32
- const resolvedTarget = path.resolve(targetPath)
33
- const targetStats = await fs.promises.stat(resolvedTarget).catch(() => null)
34
- const watchRoot = targetStats && targetStats.isDirectory()
35
- ? resolvedTarget
36
- : await nearestExistingDirectory(resolvedTarget)
37
- const filterToTarget = watchRoot !== resolvedTarget
38
-
39
- const subscription = await ParcelWatcher.subscribe(
40
- watchRoot,
41
- (error, events) => {
42
- if (error) {
43
- if (typeof options.onError === "function") {
44
- options.onError(error)
45
- }
46
- return
47
- }
48
- const normalizedEvents = Array.isArray(events) ? events : []
49
- const filteredEvents = filterToTarget
50
- ? normalizedEvents.filter((event) => event && event.path && isInside(path.resolve(event.path), resolvedTarget))
51
- : normalizedEvents
52
- if (filteredEvents.length === 0) {
53
- return
54
- }
55
- callback(filteredEvents)
56
- },
57
- {
58
- ignore: Array.isArray(options.ignore) ? options.ignore : DEFAULT_IGNORE
59
- }
60
- )
61
-
62
- return async () => {
63
- if (subscription && typeof subscription.unsubscribe === "function") {
64
- await subscription.unsubscribe().catch(() => {})
65
- }
66
- }
67
- }
68
-
69
- module.exports = {
70
- watchFs
71
- }
@@ -1,33 +0,0 @@
1
- function poll(interval, callback, options = {}) {
2
- const delay = Math.max(100, Number(interval || options.interval || 1000))
3
- let stopped = false
4
- let running = false
5
-
6
- const tick = async () => {
7
- if (stopped || running) return
8
- running = true
9
- try {
10
- await callback()
11
- } catch (error) {
12
- if (typeof options.onError === "function") {
13
- options.onError(error)
14
- }
15
- } finally {
16
- running = false
17
- }
18
- }
19
-
20
- if (options.immediate !== false) {
21
- setTimeout(tick, 0)
22
- }
23
- const timer = setInterval(tick, delay)
24
-
25
- return async () => {
26
- stopped = true
27
- clearInterval(timer)
28
- }
29
- }
30
-
31
- module.exports = {
32
- poll
33
- }
@@ -1,185 +0,0 @@
1
- const path = require("path")
2
- const WatchContext = require("./context")
3
-
4
- class WatchManager {
5
- constructor(kernel) {
6
- this.kernel = kernel
7
- this.handlers = new Map()
8
- this.sessions = new Map()
9
- }
10
-
11
- registerHandler(name, handler) {
12
- const normalized = typeof name === "string" ? name.trim() : ""
13
- if (!normalized) {
14
- throw new Error("watch handler name is required")
15
- }
16
- this.handlers.set(normalized, handler)
17
- }
18
-
19
- hasHandler(script, handlerName) {
20
- const watches = script && Array.isArray(script.watch) ? script.watch : []
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
- }
49
- }
50
-
51
- renderDeclaration(raw, memory) {
52
- let rendered = raw
53
- let pass = 0
54
- while (true) {
55
- rendered = this.kernel.template.render(rendered, memory)
56
- if (this.kernel.template.istemplate(rendered)) {
57
- pass += 1
58
- if (pass >= 4) break
59
- } else {
60
- break
61
- }
62
- }
63
- return this.kernel.template.flatten(rendered)
64
- }
65
-
66
- buildMemory({ request, script, cwd, dirname, input, args }) {
67
- return {
68
- script: this.kernel.script,
69
- input,
70
- args,
71
- cwd,
72
- dirname,
73
- uri: request.uri,
74
- self: script,
75
- kernel: this.kernel,
76
- ...this.kernel.vars
77
- }
78
- }
79
-
80
- async resolveExternalHandler(ctx, uri) {
81
- const modpath = ctx.resolveModule(uri)
82
- const loaded = await this.kernel.loader.load(modpath)
83
- let handler = loaded && loaded.resolved
84
- if (typeof handler === "function") {
85
- handler = new handler()
86
- }
87
- return handler
88
- }
89
-
90
- async startForScript(options = {}) {
91
- const script = options.script
92
- const declarations = script && Array.isArray(script.watch) ? script.watch : []
93
- if (declarations.length === 0) {
94
- return
95
- }
96
-
97
- const id = options.id
98
- if (!id) {
99
- return
100
- }
101
- await this.stop(id)
102
-
103
- const input = options.input || {}
104
- const args = options.args || input
105
- const cwd = path.resolve(options.cwd)
106
- const dirname = path.resolve(options.dirname || options.cwd)
107
- const memory = this.buildMemory({
108
- request: options.request || {},
109
- script,
110
- cwd,
111
- dirname,
112
- input,
113
- args
114
- })
115
- const disposers = []
116
-
117
- for (const rawDeclaration of declarations) {
118
- if (!rawDeclaration || typeof rawDeclaration !== "object") {
119
- continue
120
- }
121
- try {
122
- const declaration = this.renderDeclaration(rawDeclaration, memory)
123
- const ctx = new WatchContext({
124
- kernel: this.kernel,
125
- manager: this,
126
- id,
127
- cwd,
128
- dirname,
129
- request: options.request,
130
- script,
131
- declaration,
132
- input,
133
- args
134
- })
135
- const { handlerName, methodName } = this.resolveHandlerMethod(declaration)
136
- let handler = null
137
- if (handlerName) {
138
- handler = this.handlers.get(handlerName)
139
- } else if (declaration.uri) {
140
- handler = await this.resolveExternalHandler(ctx, declaration.uri)
141
- }
142
- if (!handler || !methodName || typeof handler[methodName] !== "function") {
143
- console.warn("[watch] handler not found", declaration)
144
- continue
145
- }
146
- const cleanup = await handler[methodName](ctx, declaration.params || {})
147
- if (cleanup) {
148
- disposers.push(cleanup)
149
- }
150
- } catch (error) {
151
- console.warn("[watch] failed to start", error && error.message ? error.message : error)
152
- }
153
- }
154
-
155
- if (disposers.length > 0) {
156
- this.sessions.set(id, disposers)
157
- }
158
- }
159
-
160
- async stop(id) {
161
- const normalized = typeof id === "string" ? id : ""
162
- if (!normalized || !this.sessions.has(normalized)) {
163
- return
164
- }
165
- const disposers = this.sessions.get(normalized) || []
166
- this.sessions.delete(normalized)
167
- for (const disposer of disposers.reverse()) {
168
- try {
169
- if (typeof disposer === "function") {
170
- await disposer()
171
- } else if (disposer && typeof disposer.stop === "function") {
172
- await disposer.stop()
173
- } else if (disposer && typeof disposer.dispose === "function") {
174
- await disposer.dispose()
175
- } else if (disposer && typeof disposer.unsubscribe === "function") {
176
- await disposer.unsubscribe()
177
- }
178
- } catch (error) {
179
- console.warn("[watch] cleanup failed", error && error.message ? error.message : error)
180
- }
181
- }
182
- }
183
- }
184
-
185
- module.exports = WatchManager
@@ -1,13 +0,0 @@
1
- const { createNoteFeature } = require("./notes")
2
-
3
- async function mountFeatures(options = {}) {
4
- const notes = createNoteFeature(options)
5
- await notes.start()
6
- return {
7
- notes
8
- }
9
- }
10
-
11
- module.exports = {
12
- mountFeatures
13
- }
@@ -1,41 +0,0 @@
1
- const { createNoteService } = require("./service")
2
- const registerNoteRoutes = require("./routes")
3
- const NoteWatcher = require("./watcher")
4
-
5
- function createNoteFeature(options = {}) {
6
- const { app, kernel } = options
7
- if (!app) {
8
- throw new Error("app is required")
9
- }
10
- if (!kernel) {
11
- throw new Error("kernel is required")
12
- }
13
-
14
- const service = createNoteService({
15
- kernel,
16
- taskWorkspaceLinks: options.taskWorkspaceLinks
17
- })
18
-
19
- registerNoteRoutes(app, {
20
- ...options,
21
- notes: service
22
- })
23
-
24
- if (kernel.watch && typeof kernel.watch.registerHandler === "function") {
25
- kernel.watch.registerHandler("note", new NoteWatcher({ notes: service }))
26
- }
27
-
28
- return {
29
- service,
30
- async start() {
31
- await service.start()
32
- },
33
- async stop() {
34
- await service.stop()
35
- }
36
- }
37
- }
38
-
39
- module.exports = {
40
- createNoteFeature
41
- }