pinokiod 5.3.12 → 5.3.14

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pinokiod",
3
- "version": "5.3.12",
3
+ "version": "5.3.14",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/script/index.js CHANGED
@@ -57,11 +57,11 @@ const server = new Server({
57
57
  profile: (gitRemote) => {
58
58
  return `https://pinokiocomputer.github.io/home/item?uri=${gitRemote}&display=profile`
59
59
  },
60
- site: "https://pinokiocomputer.github.io/home",
61
- discover_dark: "https://pinokiocomputer.github.io/home/app?theme=dark",
62
- discover_light: "https://pinokiocomputer.github.io/home/app",
63
- portal: "https://pinokiocomputer.github.io/home/portal",
64
- docs: "https://pinokiocomputer.github.io/program.pinokio.computer",
60
+ site: "https://pinokio.co",
61
+ discover_dark: "https://beta.pinokio.co",
62
+ discover_light: "https://beta.pinokio.co",
63
+ portal: "https://beta.pinokio.co",
64
+ docs: "https://pinokio.co/docs",
65
65
  install: "https://pinokiocomputer.github.io/program.pinokio.computer/#/?id=install",
66
66
  agent: "web",
67
67
  store: new Store()
package/server/index.js CHANGED
@@ -4818,35 +4818,10 @@ class Server {
4818
4818
  })
4819
4819
  }))
4820
4820
  this.app.get("/checkpoints/registry", ex(async (req, res) => {
4821
- const registryConnectUrl = await this.getRegistryConnectUrl().catch(() => null)
4822
- const registryUrlPrefill = typeof req.query.registry_url === "string" ? req.query.registry_url.trim() : ""
4823
- res.render("checkpoints_registry", {
4824
- registryConnectUrl,
4825
- registryUrlPrefill,
4826
- theme: this.theme,
4827
- agent: req.agent,
4828
- })
4821
+ res.status(404).send("Not found")
4829
4822
  }))
4830
4823
  this.app.post("/checkpoints/registry", ex(async (req, res) => {
4831
- const connectRaw = Object.prototype.hasOwnProperty.call(req.body || {}, "connectUrl")
4832
- ? req.body.connectUrl
4833
- : (Object.prototype.hasOwnProperty.call(req.body || {}, "registry_url")
4834
- ? req.body.registry_url
4835
- : (Object.prototype.hasOwnProperty.call(req.body || {}, "connectEndpoint")
4836
- ? req.body.connectEndpoint
4837
- : (Object.prototype.hasOwnProperty.call(req.query || {}, "connectUrl")
4838
- ? req.query.connectUrl
4839
- : (Object.prototype.hasOwnProperty.call(req.query || {}, "registry_url")
4840
- ? req.query.registry_url
4841
- : (Object.prototype.hasOwnProperty.call(req.query || {}, "connectEndpoint")
4842
- ? req.query.connectEndpoint
4843
- : "")))))
4844
- const connectUrl = connectRaw != null ? String(connectRaw).trim() : ""
4845
- await this.updateEnvironmentVars({
4846
- PINOKIO_REGISTRY_CONNECT_URL: connectUrl,
4847
- PINOKIO_REGISTRY_CONNECT_ENDPOINT: ""
4848
- })
4849
- res.json({ ok: true, connectUrl: connectUrl || null })
4824
+ res.status(404).json({ ok: false, error: "Not found" })
4850
4825
  }))
4851
4826
  this.app.get("/registry/ping.png", ex(async (_req, res) => {
4852
4827
  res.setHeader('Content-Type', 'image/png')
@@ -5111,74 +5086,7 @@ class Server {
5111
5086
  }))
5112
5087
 
5113
5088
  this.app.post("/checkpoints/publish", ex(async (req, res) => {
5114
- const registryEnabled = await this.isRegistryEnabled().catch(() => false)
5115
- if (!registryEnabled) {
5116
- res.status(404).json({ ok: false, error: "Not found" })
5117
- return
5118
- }
5119
- const snapshotRaw = typeof req.query.snapshotId === 'string' || typeof req.query.snapshotId === 'number' ? req.query.snapshotId : ''
5120
- const snapshotId = snapshotRaw === 'latest' ? 'latest' : String(snapshotRaw || '')
5121
- if (!snapshotId) {
5122
- res.status(400).json({ ok: false, error: "Missing snapshotId" })
5123
- return
5124
- }
5125
- const payload = await this.kernel.git.readCheckpointPayload(snapshotId)
5126
- if (!payload || !payload.app || !payload.hash) {
5127
- res.status(404).json({ ok: false, error: "Snapshot not found" })
5128
- return
5129
- }
5130
-
5131
- const registry = await this.getRegistryConfig()
5132
- const baseUrl = registry && registry.url ? String(registry.url).replace(/\/$/, '') : null
5133
- const apiKey = registry && registry.apiKey ? String(registry.apiKey) : null
5134
- const connectUrl = await this.getRegistryConnectUrl().catch(() => null)
5135
-
5136
- if (!baseUrl || !apiKey) {
5137
- await this.kernel.git.setCheckpointSync(payload.app, snapshotId, { status: "needs_link", at: Date.now() }).catch(() => {})
5138
- res.json({ ok: true, publish: { ok: false, code: "not_linked", connectUrl } })
5139
- return
5140
- }
5141
-
5142
- try {
5143
- await this.kernel.git.setCheckpointSync(payload.app, snapshotId, { status: "syncing", at: Date.now() })
5144
- const filePath = this.kernel.git.checkpointFilePath(payload.hash)
5145
- const checkpoint = filePath ? JSON.parse(await fs.promises.readFile(filePath, "utf8")) : null
5146
- const system = payload.system && typeof payload.system === "object" ? payload.system : {
5147
- platform: payload.platform || null,
5148
- arch: payload.arch || null,
5149
- gpu: payload.gpu || null,
5150
- ram: typeof payload.ram === "number" ? payload.ram : null,
5151
- vram: typeof payload.vram === "number" ? payload.vram : null,
5152
- }
5153
- const response = await axios.post(
5154
- `${baseUrl}/checkpoints`,
5155
- {
5156
- hash: String(payload.hash),
5157
- visibility: "public",
5158
- checkpoint,
5159
- system,
5160
- },
5161
- { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` } }
5162
- )
5163
- const remoteId = response && response.data
5164
- ? (response.data.checkpoint && response.data.checkpoint.id ? response.data.checkpoint.id : (response.data.id ? response.data.id : null))
5165
- : null
5166
- const viewUrl = await this.getRegistryViewUrl(payload.hash)
5167
- await this.kernel.git.setCheckpointSync(payload.app, snapshotId, { status: "published", at: Date.now(), remoteId })
5168
- await this.kernel.git.setCheckpointDecision(payload.app, snapshotId, "published").catch(() => {})
5169
- res.json({ ok: true, publish: { ok: true, remoteId, hash: payload.hash, url: viewUrl } })
5170
- } catch (error) {
5171
- const status = error && error.response && error.response.status ? Number(error.response.status) : null
5172
- const message = error && error.message ? error.message : String(error)
5173
- if (status === 401 || status === 403) {
5174
- await this.updateEnvironmentVars({ PINOKIO_REGISTRY_API_KEY: "" }).catch(() => {})
5175
- await this.kernel.git.setCheckpointSync(payload.app, snapshotId, { status: "needs_link", at: Date.now(), error: message }).catch(() => {})
5176
- res.json({ ok: true, publish: { ok: false, code: "not_linked", connectUrl } })
5177
- return
5178
- }
5179
- await this.kernel.git.setCheckpointSync(payload.app, snapshotId, { status: "error", at: Date.now(), error: message }).catch(() => {})
5180
- res.json({ ok: true, publish: { ok: false, code: "error", error: message } })
5181
- }
5089
+ res.status(404).json({ ok: false, error: "Not found" })
5182
5090
  }))
5183
5091
 
5184
5092
  this.app.post("/checkpoints/decision", ex(async (req, res) => {
@@ -9315,7 +9223,7 @@ class Server {
9315
9223
  title: name,
9316
9224
  url: gitRemote,
9317
9225
  //redirect_uri: "http://localhost:3001/apps/redirect?git=" + gitRemote,
9318
- redirect_uri: "https://pinokio.co/apps/redirect?git=" + gitRemote,
9226
+ redirect_uri: `${this.portal}/resolve?url=${encodeURIComponent(gitRemote)}`,
9319
9227
  platform: this.kernel.platform,
9320
9228
  theme: this.theme,
9321
9229
  agent: req.agent,
@@ -3702,12 +3702,11 @@ body.dark .snapshot-footer-input input {
3702
3702
  <iframe class='selected' src="<%=editor_tab%>"></iframe>
3703
3703
  <% } %>
3704
3704
  </main>
3705
- <% const registryEnabledValue = typeof registryEnabled === "undefined" ? false : registryEnabled; %>
3706
3705
  <% const snapshotFooterAllowed = typeof snapshotFooterEnabled === "undefined" ? false : snapshotFooterEnabled; %>
3707
3706
  <% if (snapshotFooterAllowed) { %>
3708
- <div class='snapshot-footer hidden' data-workspace="<%=name%>" data-pending-snapshot-id="<%= pendingSnapshotId ? pendingSnapshotId : '' %>" data-registry-enabled="<%= registryEnabledValue ? '1' : '0' %>">
3709
- <div class="snapshot-footer-save <%= registryEnabledValue && pendingSnapshotId ? 'hidden' : '' %>">
3710
- <div class='caption' title="<%= registryEnabledValue ? 'Saves a tiny JSON checkpoint of exact commit hashes (no files). You can publish it after saving.' : 'Saves a tiny JSON checkpoint of exact commit hashes (no files).' %>">
3707
+ <div class='snapshot-footer hidden' data-workspace="<%=name%>">
3708
+ <div class="snapshot-footer-save">
3709
+ <div class='caption' title="Saves a tiny JSON checkpoint of exact commit hashes (no files).">
3711
3710
  Snapshot installed modules information and restore later
3712
3711
  </div>
3713
3712
  <div class="snapshot-footer-actions" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
@@ -3718,15 +3717,6 @@ body.dark .snapshot-footer-input input {
3718
3717
  <button type="button" class="btn snapshot-btn-dismiss"><i class="fa-solid fa-circle-xmark"></i> Dismiss</button>
3719
3718
  </div>
3720
3719
  </div>
3721
- <% if (registryEnabledValue) { %>
3722
- <div class="snapshot-footer-publish <%= pendingSnapshotId ? '' : 'hidden' %>" aria-live="polite">
3723
- <div class="snapshot-footer-publish-text">Local save success! Publish to the registry?</div>
3724
- <div class="snapshot-footer-actions" style="display:flex; gap:10px; flex-wrap:wrap; align-items:center;">
3725
- <button type="button" class="btn btn-primary snapshot-btn-publish"><i class="fa-solid fa-cloud-arrow-up"></i> Publish Checkpoint</button>
3726
- <button type="button" class="btn snapshot-btn-later">Later</button>
3727
- </div>
3728
- </div>
3729
- <% } %>
3730
3720
  </div>
3731
3721
  <% } %>
3732
3722
  </div>
@@ -9723,22 +9713,15 @@ document.addEventListener("DOMContentLoaded", () => {
9723
9713
  if (!footer) return
9724
9714
  const saveBtn = footer.querySelector(".snapshot-btn-save")
9725
9715
  const savePanel = footer.querySelector(".snapshot-footer-save")
9726
- const publishPanel = footer.querySelector(".snapshot-footer-publish")
9727
- const publishBtn = footer.querySelector(".snapshot-btn-publish")
9728
- const laterBtn = footer.querySelector(".snapshot-btn-later")
9729
9716
  const dismissBtn = footer.querySelector(".snapshot-btn-dismiss")
9730
9717
  const howBtn = footer.querySelector(".snapshot-btn-how")
9731
- const publishText = publishPanel ? publishPanel.querySelector(".snapshot-footer-publish-text") : null
9732
- const publishActions = publishPanel ? publishPanel.querySelector(".snapshot-footer-actions") : null
9733
9718
  if (!saveBtn) return
9734
- const registryEnabled = footer.getAttribute("data-registry-enabled") === "1"
9735
9719
  const workspace = footer.getAttribute("data-workspace")
9736
9720
  if (!workspace) {
9737
9721
  footer.classList.add("hidden")
9738
9722
  return
9739
9723
  }
9740
9724
  footer.classList.add("hidden")
9741
- let pendingSnapshotId = footer.getAttribute("data-pending-snapshot-id") || ""
9742
9725
  const dismissStorageKey = `pinokio.snapshot-footer.dismissed:${workspace}`
9743
9726
  const getDismissedState = () => {
9744
9727
  try {
@@ -9763,57 +9746,29 @@ document.addEventListener("DOMContentLoaded", () => {
9763
9746
  document.documentElement.classList.remove("snapshot-footer-dismissed")
9764
9747
  }
9765
9748
  }
9766
- let savedSnapshotId = null
9767
9749
  const saveOriginal = saveBtn.innerHTML
9768
- const publishOriginal = publishBtn ? publishBtn.innerHTML : ""
9769
9750
  const setSaveLoading = (isLoading, primaryText) => {
9770
9751
  saveBtn.disabled = isLoading
9771
9752
  if (primaryText) {
9772
9753
  saveBtn.innerHTML = primaryText
9773
9754
  }
9774
9755
  }
9775
- const setPublishLoading = (isLoading, primaryText) => {
9776
- if (publishBtn) publishBtn.disabled = isLoading
9777
- if (laterBtn) laterBtn.disabled = isLoading
9778
- if (primaryText && publishBtn) {
9779
- publishBtn.innerHTML = primaryText
9780
- }
9781
- }
9782
- const showPublishPanel = () => {
9783
- if (!registryEnabled) {
9784
- footer.classList.add("hidden")
9785
- return
9786
- }
9787
- if (publishPanel) publishPanel.classList.remove("hidden")
9788
- if (savePanel) savePanel.classList.add("hidden")
9789
- }
9790
- const showSavePanel = () => {
9791
- if (savePanel) savePanel.classList.remove("hidden")
9792
- if (publishPanel) publishPanel.classList.add("hidden")
9793
- }
9794
9756
  const applySnapshotStatus = (status) => {
9795
9757
  const hasSnapshots = !!(status && status.hasSnapshots)
9796
- pendingSnapshotId = status && status.pendingSnapshotId ? String(status.pendingSnapshotId) : ""
9797
- footer.setAttribute("data-pending-snapshot-id", pendingSnapshotId)
9798
- const shouldShow = !hasSnapshots || (registryEnabled && pendingSnapshotId)
9758
+ const shouldShow = !hasSnapshots
9799
9759
  if (!shouldShow) {
9800
9760
  setDismissedClass(false)
9801
9761
  footer.classList.add("hidden")
9802
9762
  return
9803
9763
  }
9804
- const dismissed = !pendingSnapshotId && getDismissedState()
9764
+ const dismissed = getDismissedState()
9805
9765
  setDismissedClass(dismissed)
9806
9766
  if (dismissed) {
9807
9767
  footer.classList.add("hidden")
9808
9768
  return
9809
9769
  }
9810
9770
  footer.classList.remove("hidden")
9811
- if (pendingSnapshotId) {
9812
- savedSnapshotId = pendingSnapshotId
9813
- showPublishPanel()
9814
- } else {
9815
- showSavePanel()
9816
- }
9771
+ if (savePanel) savePanel.classList.remove("hidden")
9817
9772
  }
9818
9773
  const loadSnapshotStatus = async () => {
9819
9774
  try {
@@ -9888,141 +9843,7 @@ document.addEventListener("DOMContentLoaded", () => {
9888
9843
  })
9889
9844
  }
9890
9845
 
9891
- const waitForRegistryLink = async () => {
9892
- const startedAt = Date.now()
9893
- while (Date.now() - startedAt < 120000) {
9894
- try {
9895
- const s = await fetch("/api/registry/status", { method: "GET", headers: { "Accept": "application/json" } })
9896
- if (s.ok) {
9897
- const data = await s.json()
9898
- if (data && data.linked) return true
9899
- }
9900
- } catch (_) {}
9901
- await new Promise((r) => setTimeout(r, 2000))
9902
- }
9903
- return false
9904
- }
9905
-
9906
- const publishSnapshot = async (snapshotId) => {
9907
- const qs = new URLSearchParams()
9908
- qs.set("snapshotId", String(snapshotId))
9909
- const res = await fetch(`/checkpoints/publish?${qs.toString()}`, { method: "POST", headers: { "Accept": "application/json" } })
9910
- if (!res.ok) return { ok: false }
9911
- try { return await res.json() } catch (_) { return { ok: false } }
9912
- }
9913
-
9914
9846
  let isSaving = false
9915
- let isPublishing = false
9916
- const showPublishedLink = (publishUrl) => {
9917
- showPublishPanel()
9918
- if (publishText) publishText.textContent = "Published."
9919
- if (publishActions) {
9920
- publishActions.innerHTML = publishUrl
9921
- ? `<a class="btn btn-primary" href="${escapePublishUrl(publishUrl)}" target="_blank" rel="noopener"><i class="fa-solid fa-arrow-up-right-from-square"></i> View published checkpoint</a>`
9922
- : ''
9923
- }
9924
- }
9925
- const escapePublishUrl = (value) => {
9926
- if (value === null || value === undefined) {
9927
- return ''
9928
- }
9929
- return String(value).replace(/[&<>"']/g, (match) => {
9930
- switch (match) {
9931
- case '&':
9932
- return '&amp;'
9933
- case '<':
9934
- return '&lt;'
9935
- case '>':
9936
- return '&gt;'
9937
- case '"':
9938
- return '&quot;'
9939
- case '\'':
9940
- return '&#39;'
9941
- default:
9942
- return match
9943
- }
9944
- })
9945
- }
9946
-
9947
- const promptRegistryConnect = async (suggestedConnectUrl) => {
9948
- const connectHtml = `
9949
- <div class="pinokio-modal-surface">
9950
- <div class="pinokio-modal-header">
9951
- <div class="pinokio-modal-icon"><i class="fa-solid fa-link"></i></div>
9952
- <div class="pinokio-modal-heading">
9953
- <div class="pinokio-modal-title">Connect to Registry</div>
9954
- <div class="pinokio-modal-subtitle">
9955
- Enter your registry connect URL to link Pinokio, then we’ll retry the publish.
9956
- </div>
9957
- </div>
9958
- </div>
9959
- <div class="pinokio-modal-body">
9960
- <div class="pinokio-modal-form">
9961
- <div>
9962
- <label class="pinokio-modal-label" for="pinokio-registry-connect-url">Registry URL</label>
9963
- <input
9964
- id="pinokio-registry-connect-url"
9965
- class="pinokio-modal-input"
9966
- type="url"
9967
- placeholder="https://your-registry/connect/pinokio"
9968
- autocomplete="off"
9969
- />
9970
- </div>
9971
- </div>
9972
- </div>
9973
- </div>
9974
- `
9975
- const choice = await Swal.fire({
9976
- html: connectHtml,
9977
- backdrop: 'rgba(9,11,15,0.65)',
9978
- width: 'min(520px, 92vw)',
9979
- showCancelButton: true,
9980
- showConfirmButton: true,
9981
- confirmButtonText: "Open connect page",
9982
- cancelButtonText: "Cancel",
9983
- buttonsStyling: false,
9984
- focusConfirm: false,
9985
- customClass: {
9986
- popup: 'pinokio-modern-modal',
9987
- htmlContainer: 'pinokio-modern-html',
9988
- closeButton: 'pinokio-modern-close',
9989
- confirmButton: 'pinokio-modern-confirm',
9990
- cancelButton: 'pinokio-modern-cancel'
9991
- },
9992
- didOpen: () => {
9993
- const input = document.getElementById("pinokio-registry-connect-url")
9994
- if (input) {
9995
- input.value = suggestedConnectUrl || ""
9996
- try { input.focus() } catch (_) {}
9997
- try { input.select() } catch (_) {}
9998
- }
9999
- },
10000
- preConfirm: () => {
10001
- const input = document.getElementById("pinokio-registry-connect-url")
10002
- const v = input && input.value != null ? String(input.value).trim() : ""
10003
- if (!v) {
10004
- Swal.showValidationMessage("Registry URL is required")
10005
- return false
10006
- }
10007
- return v
10008
- }
10009
- })
10010
- if (!choice || !choice.isConfirmed) return false
10011
- const connectUrl = choice.value ? String(choice.value).trim() : ""
10012
- if (!connectUrl) return false
10013
- try {
10014
- window.open(connectUrl, "pinokio-registry-connect")
10015
- } catch (_) {
10016
- window.location.href = connectUrl
10017
- }
10018
- const linked = await waitForRegistryLink()
10019
- if (!linked) {
10020
- Swal.fire({ icon: "error", title: "Not connected", text: "Could not confirm the registry connection." })
10021
- return false
10022
- }
10023
- return true
10024
- }
10025
-
10026
9847
  const handleSave = async (event) => {
10027
9848
  event.preventDefault()
10028
9849
  if (isSaving) return
@@ -10108,10 +9929,8 @@ document.addEventListener("DOMContentLoaded", () => {
10108
9929
  Swal.fire({ icon: "error", title: "Error", text: "Save failed" })
10109
9930
  return
10110
9931
  }
10111
- savedSnapshotId = snapshotId
10112
- setSaveLoading(false, saveOriginal)
9932
+ setSaveLoading(false, `<i class="fa-solid fa-circle-check"></i> Saved`)
10113
9933
  saveBtn.disabled = true
10114
- showPublishPanel()
10115
9934
  } catch (_) {
10116
9935
  setSaveLoading(false, saveOriginal)
10117
9936
  Swal.fire({ icon: "error", title: "Error", text: "Save failed" })
@@ -10120,72 +9939,6 @@ document.addEventListener("DOMContentLoaded", () => {
10120
9939
  }
10121
9940
  }
10122
9941
 
10123
- const handlePublish = async (event) => {
10124
- event.preventDefault()
10125
- if (isPublishing || !savedSnapshotId) return
10126
- isPublishing = true
10127
- setPublishLoading(true, `<i class="fa-solid fa-circle-notch fa-spin"></i> Publishing...`)
10128
- try {
10129
- const published = await publishSnapshot(savedSnapshotId)
10130
- if (published && published.publish && published.publish.ok) {
10131
- const publishUrl = published && published.publish && published.publish.url ? String(published.publish.url) : ""
10132
- showPublishedLink(publishUrl)
10133
- return
10134
- }
10135
- if (published && published.publish && published.publish.code === "not_linked") {
10136
- const suggestedConnectUrl = published.publish.connectUrl ? String(published.publish.connectUrl) : ""
10137
- const linked = await promptRegistryConnect(suggestedConnectUrl)
10138
- if (!linked) {
10139
- setPublishLoading(false, publishOriginal)
10140
- return
10141
- }
10142
- const retry = await publishSnapshot(savedSnapshotId)
10143
- if (retry && retry.publish && retry.publish.ok) {
10144
- footer.classList.add("hidden")
10145
- return
10146
- }
10147
- const retryMsg = retry && retry.publish && retry.publish.error ? retry.publish.error : "Publish failed"
10148
- Swal.fire({ icon: "error", title: "Error", text: retryMsg })
10149
- setPublishLoading(false, publishOriginal)
10150
- return
10151
- }
10152
- const msg = published && published.publish && published.publish.error ? published.publish.error : "Publish failed"
10153
- Swal.fire({ icon: "error", title: "Error", text: msg })
10154
- setPublishLoading(false, publishOriginal)
10155
- } catch (_) {
10156
- setPublishLoading(false, publishOriginal)
10157
- Swal.fire({ icon: "error", title: "Error", text: "Publish failed" })
10158
- } finally {
10159
- isPublishing = false
10160
- }
10161
- }
10162
-
10163
- const handleLater = async (event) => {
10164
- event.preventDefault()
10165
- if (!savedSnapshotId) {
10166
- footer.classList.add("hidden")
10167
- return
10168
- }
10169
- setPublishLoading(true)
10170
- try {
10171
- const qs = new URLSearchParams()
10172
- qs.set("snapshotId", String(savedSnapshotId))
10173
- qs.set("decision", "later")
10174
- const res = await fetch(`/checkpoints/decision?${qs.toString()}`, {
10175
- method: "POST",
10176
- headers: { "Accept": "application/json" }
10177
- })
10178
- const payload = res.ok ? await res.json().catch(() => null) : null
10179
- if (!payload || !payload.ok) {
10180
- throw new Error("Failed to save decision")
10181
- }
10182
- footer.classList.add("hidden")
10183
- } catch (_) {
10184
- Swal.fire({ icon: "error", title: "Error", text: "Could not save decision" })
10185
- setPublishLoading(false, publishOriginal)
10186
- }
10187
- }
10188
-
10189
9942
  const handleDismiss = (event) => {
10190
9943
  event.preventDefault()
10191
9944
  setDismissedState(true)
@@ -10194,8 +9947,6 @@ document.addEventListener("DOMContentLoaded", () => {
10194
9947
  }
10195
9948
 
10196
9949
  saveBtn.addEventListener("click", handleSave)
10197
- if (registryEnabled && publishBtn) publishBtn.addEventListener("click", handlePublish)
10198
- if (registryEnabled && laterBtn) laterBtn.addEventListener("click", handleLater)
10199
9950
  if (dismissBtn) dismissBtn.addEventListener("click", handleDismiss)
10200
9951
  })
10201
9952
  </script>
@@ -403,12 +403,10 @@ body.dark .backup-loading .backup-loading-text {
403
403
  window.backupItems = <%- JSON.stringify(items || []) %>;
404
404
  window.backupAutoInstall = <%- JSON.stringify(autoInstall || null) %>;
405
405
  window.backupImportError = <%- JSON.stringify(importError || null) %>;
406
- window.registryEnabled = <%- JSON.stringify(!!registryEnabled) %>;
407
406
  document.addEventListener("DOMContentLoaded", () => {
408
407
  const items = Array.isArray(window.backupItems) ? window.backupItems : []
409
408
  const autoInstall = window.backupAutoInstall && typeof window.backupAutoInstall === "object" ? window.backupAutoInstall : null
410
409
  const importError = typeof window.backupImportError === "string" && window.backupImportError.trim() ? window.backupImportError.trim() : null
411
- const registryEnabled = window.registryEnabled === true
412
410
 
413
411
  const escapeHtml = (value) => {
414
412
  const str = value == null ? '' : String(value)
@@ -508,12 +506,7 @@ document.addEventListener("DOMContentLoaded", () => {
508
506
  metaParts.push(`${repos.length} repo${repos.length === 1 ? '' : 's'}`)
509
507
  if (when) metaParts.push(when)
510
508
  const metaText = metaParts.join(" · ")
511
- const syncStatus = snap && snap.sync && snap.sync.status ? String(snap.sync.status) : null
512
- const publishMarkup = registryEnabled
513
- ? (syncStatus === "published"
514
- ? `<span class="badge">Cloud saved</span>`
515
- : `<button type="button" class="url-modal-button confirm snapshot-publish" data-snapshot-id="${snap.id}">Save to Cloud</button>`)
516
- : ""
509
+ const publishMarkup = ""
517
510
  const installed = Array.isArray(installedNames) ? installedNames : []
518
511
  const installedMarkup = installed.length
519
512
  ? `<div class="installed-list"><span>Installed:</span> ${installed.map((name) => `<a href="/p/${encodeURIComponent(name)}" class="installed-link">/p/${escapeHtml(name)}</a>`).join(", ")}</div>`
@@ -594,8 +587,7 @@ document.addEventListener("DOMContentLoaded", () => {
594
587
  const closeBtn = container.querySelector('.url-modal-close')
595
588
  const installButtons = Array.from(container.querySelectorAll(".snapshot-install"))
596
589
  const deleteButtons = Array.from(container.querySelectorAll(".snapshot-delete"))
597
- const publishButtons = Array.from(container.querySelectorAll(".snapshot-publish"))
598
- const actionButtons = installButtons.concat(deleteButtons, publishButtons)
590
+ const actionButtons = installButtons.concat(deleteButtons)
599
591
  const loadingOverlay = container.querySelector("#backup-loading")
600
592
  const loadingText = loadingOverlay ? loadingOverlay.querySelector(".backup-loading-text") : null
601
593
  const defaultLoadingText = loadingText ? loadingText.textContent : ""
@@ -881,157 +873,6 @@ document.addEventListener("DOMContentLoaded", () => {
881
873
  }
882
874
  }, 0)
883
875
 
884
- const waitForRegistryLink = async () => {
885
- const startedAt = Date.now()
886
- while (Date.now() - startedAt < 120000) {
887
- try {
888
- const s = await fetch("/api/registry/status", { method: "GET", headers: { "Accept": "application/json" } })
889
- if (s.ok) {
890
- const data = await s.json()
891
- if (data && data.linked) return true
892
- }
893
- } catch (_) {}
894
- await new Promise((r) => setTimeout(r, 2000))
895
- }
896
- return false
897
- }
898
- const publishSnapshot = async (snapshotId, allowConnect = true) => {
899
- const btn = publishButtons.find((b) => b.dataset.snapshotId === String(snapshotId))
900
- const original = btn ? btn.textContent : "Save to Cloud"
901
- if (btn) {
902
- btn.disabled = true
903
- btn.textContent = "Saving..."
904
- }
905
- try {
906
- const qs = new URLSearchParams()
907
- qs.set("snapshotId", String(snapshotId))
908
- const res = await fetch(`/checkpoints/publish?${qs.toString()}`, { method: "POST", headers: { "Accept": "application/json" } })
909
- const payload = res && res.ok ? await res.json() : null
910
- if (payload && payload.publish && payload.publish.ok) {
911
- const publishUrl = payload && payload.publish && payload.publish.url ? String(payload.publish.url) : ""
912
- const linkHtml = publishUrl
913
- ? `<a href="${escapeHtml(publishUrl)}" target="_blank" rel="noopener">View published checkpoint</a>`
914
- : ""
915
- Swal.fire({
916
- icon: "success",
917
- title: "Saved to Cloud",
918
- html: linkHtml || undefined,
919
- showConfirmButton: true,
920
- confirmButtonText: "Close"
921
- }).then(() => {
922
- window.location.reload()
923
- })
924
- return
925
- }
926
- if (allowConnect && payload && payload.publish && payload.publish.code === "not_linked") {
927
- const suggestedConnectUrl = payload.publish.connectUrl ? String(payload.publish.connectUrl) : ""
928
- const connectUrl = await new Promise((resolve) => {
929
- let settled = false
930
- const cleanup = (value) => {
931
- if (settled) return
932
- settled = true
933
- try { Swal.close() } catch (_) {}
934
- resolve(value)
935
- }
936
- const html = `
937
- <div class="backup-modal-wrapper">
938
- <div class="url-modal-content backup-modal-content" role="dialog" aria-modal="true" aria-labelledby="registry-connect-title" aria-describedby="registry-connect-description">
939
- <button type="button" class="url-modal-close" aria-label="Close">&times;</button>
940
- <div class="backup-modal-header">
941
- <h3 id="registry-connect-title">Connect to Registry</h3>
942
- <p class="backup-modal-description" id="registry-connect-description">Enter your registry connect URL to link Pinokio, then we’ll retry the cloud save.</p>
943
- </div>
944
- <div class="backup-modal folder-row" style="margin-top: 0;">
945
- <label for="registry-connect-url">Registry URL</label>
946
- <input id="registry-connect-url" class="url-modal-input" type="url" value="${escapeAttr(suggestedConnectUrl)}" placeholder="https://your-registry/connect/pinokio" autocomplete="off" />
947
- <div class="folder-hint" id="registry-connect-hint"></div>
948
- </div>
949
- <div class="url-modal-actions">
950
- <button type="button" class="url-modal-button cancel" data-action="cancel">Cancel</button>
951
- <button type="button" class="url-modal-button confirm" data-action="confirm">Open connect page</button>
952
- </div>
953
- </div>
954
- </div>
955
- `
956
- Swal.fire({
957
- html,
958
- showConfirmButton: false,
959
- showCancelButton: false,
960
- allowOutsideClick: true,
961
- customClass: { popup: 'backup-modal-shell' },
962
- didOpen: () => {
963
- const container = Swal.getHtmlContainer()
964
- if (!container) return
965
- const input = container.querySelector("#registry-connect-url")
966
- const hint = container.querySelector("#registry-connect-hint")
967
- const closeBtn = container.querySelector(".url-modal-close")
968
- const cancelBtn = container.querySelector('[data-action="cancel"]')
969
- const confirmBtn = container.querySelector('[data-action="confirm"]')
970
- const validate = () => {
971
- const val = input && input.value != null ? String(input.value).trim() : ""
972
- if (!val) {
973
- if (hint) hint.textContent = "Registry URL required."
974
- return null
975
- }
976
- if (hint) hint.textContent = ""
977
- return val
978
- }
979
- if (input) {
980
- try { input.focus() } catch (_) {}
981
- input.addEventListener("keydown", (e) => {
982
- if (e.key === "Enter") {
983
- e.preventDefault()
984
- const val = validate()
985
- if (val) cleanup(val)
986
- }
987
- })
988
- }
989
- if (confirmBtn) {
990
- confirmBtn.addEventListener("click", () => {
991
- const val = validate()
992
- if (val) cleanup(val)
993
- })
994
- }
995
- if (cancelBtn) cancelBtn.addEventListener("click", () => cleanup(null))
996
- if (closeBtn) closeBtn.addEventListener("click", () => cleanup(null))
997
- }
998
- }).then(() => cleanup(null))
999
- })
1000
-
1001
- if (!connectUrl) return
1002
- try {
1003
- window.open(connectUrl, "pinokio-registry-connect")
1004
- } catch (_) {
1005
- window.location.href = connectUrl
1006
- return
1007
- }
1008
- const linked = await waitForRegistryLink()
1009
- if (linked) {
1010
- await publishSnapshot(snapshotId, false)
1011
- return
1012
- }
1013
- Swal.fire({ icon: "error", title: "Not connected", text: "Could not confirm the registry connection." })
1014
- return
1015
- }
1016
- const msg = payload && payload.publish && payload.publish.error ? payload.publish.error : "Cloud save failed"
1017
- Swal.fire({ icon: "error", title: "Error", text: msg })
1018
- } catch (error) {
1019
- Swal.fire({ icon: "error", title: "Error", text: error && error.message ? error.message : "Cloud save failed" })
1020
- } finally {
1021
- if (btn) {
1022
- btn.disabled = false
1023
- btn.textContent = original
1024
- }
1025
- }
1026
- }
1027
-
1028
- publishButtons.forEach((btn) => {
1029
- btn.addEventListener("click", () => {
1030
- const snapshotId = btn.getAttribute("data-snapshot-id") || ''
1031
- if (snapshotId) publishSnapshot(snapshotId)
1032
- })
1033
- })
1034
-
1035
876
  const handleCancel = () => Swal.close()
1036
877
  closeBtn && closeBtn.addEventListener("click", handleCancel)
1037
878
  }
@@ -1076,11 +917,6 @@ document.addEventListener("DOMContentLoaded", () => {
1076
917
  <i class="fa-solid fa-folder-open"></i> Open checkpoints folder
1077
918
  </button>
1078
919
  <% } %>
1079
- <% if (registryEnabled) { %>
1080
- <a class="btn" href="/checkpoints/registry">
1081
- <i class="fa-solid fa-link"></i> Registry settings
1082
- </a>
1083
- <% } %>
1084
920
  </div>
1085
921
  <% if (items && items.length > 0) { %>
1086
922
  <% items.forEach((item) => { %>