pinokiod 7.3.15 → 7.4.1

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 (52) hide show
  1. package/kernel/api/index.js +111 -15
  2. package/kernel/api/script/index.js +10 -0
  3. package/kernel/autolaunch.js +111 -0
  4. package/kernel/bin/ffmpeg.js +9 -791
  5. package/kernel/environment.js +89 -1
  6. package/kernel/git.js +9 -19
  7. package/kernel/index.js +142 -43
  8. package/kernel/launch_requirements.js +1115 -0
  9. package/kernel/procs.js +1 -1
  10. package/kernel/ready.js +231 -0
  11. package/kernel/script.js +16 -0
  12. package/kernel/shells.js +9 -1
  13. package/kernel/workspace_status.js +111 -45
  14. package/package.json +2 -3
  15. package/server/autolaunch.js +725 -0
  16. package/server/index.js +244 -411
  17. package/server/views/app.ejs +267 -160
  18. package/server/views/autolaunch.ejs +363 -75
  19. package/server/views/index.ejs +550 -26
  20. package/server/views/partials/app_autolaunch_dependency_events.ejs +99 -0
  21. package/server/views/partials/app_autolaunch_dependency_save.ejs +10 -0
  22. package/server/views/partials/app_autolaunch_dependency_styles.ejs +357 -0
  23. package/server/views/partials/app_autolaunch_modal_helpers.ejs +347 -0
  24. package/server/views/partials/autolaunch_dependency_helpers.ejs +204 -0
  25. package/server/views/partials/autolaunch_dependency_save.ejs +13 -0
  26. package/server/views/partials/autolaunch_dependency_styles.ejs +347 -0
  27. package/server/views/partials/home_action_modal.ejs +4 -1
  28. package/server/views/partials/launch_requirements_status_client.ejs +271 -0
  29. package/server/views/partials/launch_requirements_status_styles.ejs +171 -0
  30. package/server/views/partials/launch_settings_dependency_save_factory.ejs +45 -0
  31. package/server/views/partials/launch_settings_dependency_script_loader_factory.ejs +25 -0
  32. package/server/views/terminal.ejs +196 -2
  33. package/test/home-autolaunch-live-ui.test.js +455 -0
  34. package/test/launch-requirements-browser.test.js +579 -0
  35. package/test/launch-requirements-contract-coverage.test.js +627 -0
  36. package/test/launch-requirements-real-browser.js +625 -0
  37. package/test/launch-requirements-status-client.test.js +132 -0
  38. package/test/launch-requirements.test.js +1806 -0
  39. package/test/launch-settings-ui.test.js +370 -0
  40. package/test/procs.test.js +49 -0
  41. package/test/ready-state.test.js +49 -0
  42. package/test/server-autolaunch.test.js +1052 -0
  43. package/test/startup-git-index-benchmark.js +409 -0
  44. package/test/startup-git-index-browser.js +320 -0
  45. package/test/startup-git-index-performance.test.js +380 -0
  46. package/test/startup-git-index-refactor.test.js +450 -0
  47. package/test/startup-git-index-route.test.js +588 -0
  48. package/test/universal-launcher.smoke.spec.js +10 -9
  49. package/test/workspace-gitignore-benchmark.js +815 -0
  50. package/test/workspace-gitignore-path-scoped.test.js +256 -0
  51. package/script/verify-ffmpeg.js +0 -459
  52. package/spec/INSTRUCTION_SYNC.md +0 -432
@@ -6,6 +6,81 @@ const ManagedSkills = require('./managed_skills')
6
6
  const TEMP_ENV_KEYS = ["TMP", "TEMP", "TMPDIR", "PIP_TMPDIR"]
7
7
  const CACHE_ENV_KEYS = ["UV_CACHE_DIR", "PIP_CACHE_DIR"]
8
8
  const CACHE_PREFLIGHT_KEYS = TEMP_ENV_KEYS.concat(CACHE_ENV_KEYS)
9
+ const SCRIPT_AUTOLAUNCH_KEY = "PINOKIO_SCRIPT_AUTOLAUNCH"
10
+ const SCRIPT_AUTOLAUNCH_ENABLED_KEY = "PINOKIO_SCRIPT_AUTOLAUNCH_ENABLED"
11
+ const SCRIPT_REQUIREMENTS_KEY = "PINOKIO_SCRIPT_REQUIRES"
12
+
13
+ const parseBooleanFlag = (value) => {
14
+ if (typeof value !== "string") {
15
+ return null
16
+ }
17
+ const normalized = value.trim().toLowerCase()
18
+ if (!normalized) {
19
+ return null
20
+ }
21
+ if (["1", "true", "yes", "on"].includes(normalized)) {
22
+ return true
23
+ }
24
+ if (["0", "false", "no", "off"].includes(normalized)) {
25
+ return false
26
+ }
27
+ return null
28
+ }
29
+
30
+ const getScriptAutolaunch = (env) => {
31
+ return typeof (env && env[SCRIPT_AUTOLAUNCH_KEY]) === "string"
32
+ ? env[SCRIPT_AUTOLAUNCH_KEY].trim()
33
+ : ""
34
+ }
35
+
36
+ const getScriptAutolaunchEnabled = (env) => {
37
+ const script = getScriptAutolaunch(env)
38
+ if (!script) {
39
+ return false
40
+ }
41
+ const explicit = parseBooleanFlag(env && env[SCRIPT_AUTOLAUNCH_ENABLED_KEY])
42
+ return explicit === null ? true : explicit
43
+ }
44
+
45
+ const parseAutolaunchList = (value) => {
46
+ if (typeof value !== "string") {
47
+ return []
48
+ }
49
+ const seen = new Set()
50
+ const values = []
51
+ for (const part of value.split(",")) {
52
+ const raw = part.trim()
53
+ if (!raw) {
54
+ continue
55
+ }
56
+ let id = raw
57
+ try {
58
+ id = decodeURIComponent(raw)
59
+ } catch (_) {}
60
+ id = String(id || "").trim()
61
+ if (!id || seen.has(id)) {
62
+ continue
63
+ }
64
+ seen.add(id)
65
+ values.push(id)
66
+ }
67
+ return values
68
+ }
69
+
70
+ const formatAutolaunchList = (ids) => {
71
+ if (!Array.isArray(ids)) {
72
+ return ""
73
+ }
74
+ return ids
75
+ .map((id) => String(id || "").trim())
76
+ .filter(Boolean)
77
+ .map((id) => encodeURIComponent(id))
78
+ .join(",")
79
+ }
80
+
81
+ const getScriptRequirements = (env) => {
82
+ return parseAutolaunchList(env && env[SCRIPT_REQUIREMENTS_KEY])
83
+ }
9
84
 
10
85
  const formatCachePreflightError = (error) => {
11
86
  if (!error) {
@@ -279,6 +354,19 @@ const ENVS = async () => {
279
354
  "#",
280
355
  "##########################################################################",
281
356
  ]
357
+ }, {
358
+ type: ["app"],
359
+ key: SCRIPT_REQUIREMENTS_KEY,
360
+ val: "",
361
+ comment: [
362
+ "##########################################################################",
363
+ "#",
364
+ "# PINOKIO_SCRIPT_REQUIRES",
365
+ "# comma-separated local app folder IDs this app requires before launch",
366
+ "# requirements do not automatically enable run-at-startup for those apps",
367
+ "#",
368
+ "##########################################################################",
369
+ ]
282
370
  }, {
283
371
  type: ["system"],
284
372
  key: "PINOKIO_SHARE_VAR",
@@ -946,4 +1034,4 @@ const init = async (options, kernel) => {
946
1034
  env_path: current
947
1035
  }
948
1036
  }
949
- module.exports = { ENV, get, get2, init_folders, ensurePinokioCacheDirs, requirements, init, get_root }
1037
+ module.exports = { ENV, get, get2, init_folders, ensurePinokioCacheDirs, requirements, init, get_root, SCRIPT_AUTOLAUNCH_KEY, SCRIPT_AUTOLAUNCH_ENABLED_KEY, SCRIPT_REQUIREMENTS_KEY, getScriptAutolaunch, getScriptAutolaunchEnabled, parseAutolaunchList, formatAutolaunchList, getScriptRequirements }
package/kernel/git.js CHANGED
@@ -228,16 +228,6 @@ class Git {
228
228
  return fs.promises.copyFile(src, dest)
229
229
  }))
230
230
 
231
- // best-effort: clear any stale index.lock files across all known repos at startup
232
- try {
233
- const apiRoot = this.kernel.path("api")
234
- const repos = await this.repos(apiRoot)
235
- for (const repo of repos) {
236
- if (repo && repo.dir) {
237
- await this.clearStaleLock(repo.dir)
238
- }
239
- }
240
- } catch (_) {}
241
231
  }
242
232
  checkpointsDir() {
243
233
  return path.resolve(this.kernel.homedir, "checkpoints")
@@ -1326,6 +1316,7 @@ class Git {
1326
1316
  const gitParentPath = path.dirname(gitPath)
1327
1317
  const gitParentRelPath = path.relative(this.kernel.path("api"), gitParentPath)
1328
1318
  let dir = path.dirname(gitPath)
1319
+ await this.clearStaleLock(dir)
1329
1320
  let display_name
1330
1321
  let main
1331
1322
  if (gitRelPathSansGit === ".") {
@@ -1457,15 +1448,6 @@ class Git {
1457
1448
  async findRepoRootForPath(targetPath) {
1458
1449
  if (!targetPath || typeof targetPath !== "string") return null
1459
1450
  const absoluteTarget = path.resolve(targetPath)
1460
- const cachedRoots = Array.from(this.dirs)
1461
- .map((dir) => path.resolve(dir))
1462
- .sort((a, b) => b.length - a.length)
1463
- for (const root of cachedRoots) {
1464
- if (absoluteTarget === root || absoluteTarget.startsWith(`${root}${path.sep}`)) {
1465
- return root
1466
- }
1467
- }
1468
-
1469
1451
  let probe = await this.findExistingAncestor(absoluteTarget)
1470
1452
  if (!probe) return null
1471
1453
  try {
@@ -1488,6 +1470,14 @@ class Git {
1488
1470
  this.dirs.add(probe)
1489
1471
  return probe
1490
1472
  }
1473
+ const cachedRoots = Array.from(this.dirs)
1474
+ .map((dir) => path.resolve(dir))
1475
+ .sort((a, b) => b.length - a.length)
1476
+ for (const root of cachedRoots) {
1477
+ if (absoluteTarget === root || absoluteTarget.startsWith(`${root}${path.sep}`)) {
1478
+ return root
1479
+ }
1480
+ }
1491
1481
  return null
1492
1482
  }
1493
1483
  getManagedRepairTarget(targetPath) {
package/kernel/index.js CHANGED
@@ -40,6 +40,9 @@ const Git = require('./git')
40
40
  const Connect = require('./connect')
41
41
  const Favicon = require('./favicon')
42
42
  const AppLauncher = require('./app_launcher')
43
+ const ReadyState = require('./ready')
44
+ const Autolaunch = require('./autolaunch')
45
+ const LaunchRequirements = require('./launch_requirements')
43
46
  const { DownloaderHelper } = require('node-downloader-helper');
44
47
  const { ProxyAgent } = require('proxy-agent');
45
48
  const fakeUa = require('fake-useragent');
@@ -92,6 +95,11 @@ class Kernel {
92
95
  this.torch_backend = "cpu"
93
96
  this.vram = 0
94
97
  this.ram = 0
98
+ this.readyState = new ReadyState(this)
99
+ this.app_ready_status = this.readyState.status
100
+ this.launchRequirements = new LaunchRequirements(this)
101
+ this.autolaunch = new Autolaunch(this)
102
+ this.autolaunch_status = this.autolaunch.startupStatus()
95
103
  this.sysReady = new Promise((resolve) => {
96
104
  this._resolveSysReady = resolve
97
105
  })
@@ -110,6 +118,130 @@ class Kernel {
110
118
  })
111
119
  return response
112
120
  }
121
+ normalizeAppId(value) {
122
+ return this.readyState.normalizeAppId(value)
123
+ }
124
+ getAppIdForLaunchPath(launchPath) {
125
+ return this.readyState.getAppIdForLaunchPath(launchPath)
126
+ }
127
+ getAppRelativeLaunchScript(appId, launchPath) {
128
+ return this.readyState.getAppRelativeLaunchScript(appId, launchPath)
129
+ }
130
+ resolveReadyScriptPath(uri, cwd) {
131
+ return this.readyState.resolveScriptPath(uri, cwd)
132
+ }
133
+ isScriptReady(scriptPath) {
134
+ return this.readyState.isScriptReady(scriptPath)
135
+ }
136
+ getScriptProgress(scriptPath) {
137
+ return this.readyState.getScriptProgress(scriptPath)
138
+ }
139
+ ready(requestOrUri) {
140
+ return this.readyState.ready(requestOrUri)
141
+ }
142
+ hasLaunchRequirementRuntime(launchPath) {
143
+ return !!(
144
+ this.launchRequirements &&
145
+ typeof this.launchRequirements.hasRuntimeForLaunchPath === "function" &&
146
+ this.launchRequirements.hasRuntimeForLaunchPath(launchPath)
147
+ )
148
+ }
149
+ markAppLaunchStarted(launchPath) {
150
+ const status = this.readyState.markStarted(launchPath)
151
+ const appId = this.readyState.getAppIdForLaunchPath(launchPath)
152
+ if (this.hasLaunchRequirementRuntime(launchPath) && this.launchRequirements && typeof this.launchRequirements.markStarted === "function") {
153
+ if (typeof this.launchRequirements.markStartupStarted === "function") {
154
+ this.launchRequirements.markStartupStarted(appId)
155
+ }
156
+ this.launchRequirements.markStarted(launchPath)
157
+ }
158
+ return status
159
+ }
160
+ markAppLaunchProgress(launchPath, current, total) {
161
+ const progress = this.readyState.markProgress(launchPath, current, total)
162
+ if (progress && this.hasLaunchRequirementRuntime(launchPath) && this.launchRequirements && typeof this.launchRequirements.markProgress === "function") {
163
+ this.launchRequirements.markProgress(launchPath, progress)
164
+ }
165
+ return progress
166
+ }
167
+ markAppLaunchReady(launchPath) {
168
+ const status = this.readyState.markReady(launchPath)
169
+ const appId = this.readyState.getAppIdForLaunchPath(launchPath)
170
+ if (this.hasLaunchRequirementRuntime(launchPath) && this.launchRequirements && typeof this.launchRequirements.markDone === "function") {
171
+ if (typeof this.launchRequirements.markStartupReady === "function") {
172
+ this.launchRequirements.markStartupReady(appId)
173
+ }
174
+ this.launchRequirements.markDone(launchPath)
175
+ }
176
+ return status
177
+ }
178
+ markAppLaunchFailed(launchPath, error) {
179
+ return this.readyState.markFailed(launchPath, error)
180
+ }
181
+ markAppLaunchStopped(launchPath, options = {}) {
182
+ const status = this.readyState.markStopped(launchPath)
183
+ if (options && options.internal_completion) {
184
+ return status
185
+ }
186
+ const appId = this.readyState.getAppIdForLaunchPath(launchPath)
187
+ if (this.hasLaunchRequirementRuntime(launchPath) && this.launchRequirements && typeof this.launchRequirements.cancel === "function") {
188
+ if (typeof this.launchRequirements.markStartupStopped === "function") {
189
+ this.launchRequirements.markStartupStopped(appId)
190
+ }
191
+ const cancelled = this.launchRequirements.cancel(launchPath)
192
+ if (!cancelled && typeof this.launchRequirements.markDone === "function") {
193
+ this.launchRequirements.markDone(launchPath)
194
+ }
195
+ if (typeof this.launchRequirements.clearRelated === "function") {
196
+ this.launchRequirements.clearRelated(appId)
197
+ }
198
+ }
199
+ return status
200
+ }
201
+ beginLaunchOperation(launchPath, meta = {}) {
202
+ return this.launchRequirements && typeof this.launchRequirements.beginLaunchOperation === "function"
203
+ ? this.launchRequirements.beginLaunchOperation(launchPath, meta)
204
+ : null
205
+ }
206
+ endLaunchOperation(token) {
207
+ return this.launchRequirements && typeof this.launchRequirements.endLaunchOperation === "function"
208
+ ? this.launchRequirements.endLaunchOperation(token)
209
+ : false
210
+ }
211
+ isAppReady(appId) {
212
+ return this.readyState.isAppReady(appId)
213
+ }
214
+ collectAutolaunchCandidates() {
215
+ return this.autolaunch.collectCandidates()
216
+ }
217
+ runAutolaunchScheduler() {
218
+ return this.autolaunch.runScheduler()
219
+ }
220
+ ensureLaunchRequirements(launchPath, options = {}) {
221
+ return this.launchRequirements.ensureForLaunchPath(launchPath, options)
222
+ }
223
+ async hasLaunchRequirementConfig(launchPath) {
224
+ if (!launchPath) {
225
+ return false
226
+ }
227
+ const appId = this.getAppIdForLaunchPath(launchPath)
228
+ if (!appId) {
229
+ return false
230
+ }
231
+ const env = await Environment.get(this.path("api", appId), this)
232
+ return !!(
233
+ (typeof env.PINOKIO_SCRIPT_AUTOLAUNCH === "string" && env.PINOKIO_SCRIPT_AUTOLAUNCH.trim()) &&
234
+ (typeof env.PINOKIO_SCRIPT_REQUIRES === "string" && env.PINOKIO_SCRIPT_REQUIRES.trim())
235
+ )
236
+ }
237
+ clearLaunchRequirementsStatus(appId) {
238
+ if (this.launchRequirements && typeof this.launchRequirements.clearRelated === "function") {
239
+ this.launchRequirements.clearRelated(appId)
240
+ }
241
+ }
242
+ getLaunchRequirementsStatus(appId) {
243
+ return this.launchRequirements.getStatus(appId)
244
+ }
113
245
  async dns(request) {
114
246
  let config
115
247
  let api_path
@@ -1091,13 +1223,7 @@ class Kernel {
1091
1223
  // Initialize core tools
1092
1224
  this.bin.init().then(() => {
1093
1225
  if (this.homedir) {
1094
- this.git.init().then(() => {
1095
- this.git.index(this).then(() => {
1096
- //console.log(this.git.mapping)
1097
- }).catch((err) => {
1098
- console.warn("Git index error:", err && err.message ? err.message : err)
1099
- })
1100
- }).catch((err) => {
1226
+ this.git.init().catch((err) => {
1101
1227
  console.warn("Git init error:", err && err.message ? err.message : err)
1102
1228
  })
1103
1229
  this.shell.init().then(async () => {
@@ -1151,42 +1277,15 @@ class Kernel {
1151
1277
 
1152
1278
  // get env
1153
1279
  if (!this.launch_complete) {
1154
- let interval = setInterval(async () => {
1155
- try {
1156
- if (this.i) {
1157
- for (let api of this.i.api) {
1158
- let env_path = path.resolve(this.api.userdir, api.path)
1159
- let e = await Environment.get(env_path, this)
1160
- if (e.PINOKIO_SCRIPT_AUTOLAUNCH && e.PINOKIO_SCRIPT_AUTOLAUNCH.trim().length > 0) {
1161
- let autolaunch_path = path.resolve(env_path, e.PINOKIO_SCRIPT_AUTOLAUNCH)
1162
- let exists = await this.exists(autolaunch_path)
1163
- if (exists) {
1164
- this.api.process({
1165
- uri: autolaunch_path,
1166
- input: {}
1167
- // client: req.client,
1168
- // caller: req.parent.path,
1169
- }, (r) => {
1170
- console.log({ autolaunch_path, r })
1171
- // resolve(r.input)
1172
- }).catch((err) => {
1173
- console.warn('[Kernel.init] autolaunch process failed:', err && err.message ? err.message : err)
1174
- })
1175
- } else {
1176
- console.log("SCRIPT DOES NOT EXIST. Ignoring.", autolaunch_path)
1177
- }
1178
- }
1179
- }
1180
- clearInterval(interval)
1181
- setTimeout(() => {
1182
- this.launch_complete = true
1183
- console.log("SETTING launch complete", this.launch_complete)
1184
- }, 2000)
1185
- }
1186
- } catch (err) {
1187
- console.warn('[Kernel.init] autolaunch loop failed:', err && err.message ? err.message : err)
1188
- }
1189
- }, 1000)
1280
+ this.runAutolaunchScheduler().catch((err) => {
1281
+ console.warn('[Kernel.init] autolaunch scheduler failed:', err && err.message ? err.message : err)
1282
+ this.autolaunch.setStatus({
1283
+ running: false,
1284
+ error: err && err.message ? err.message : String(err || "Autolaunch failed"),
1285
+ apps: {}
1286
+ })
1287
+ this.launch_complete = true
1288
+ })
1190
1289
  }
1191
1290
  }
1192
1291
  }).catch((err) => {