pinokiod 5.3.13 → 5.3.15

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.13",
3
+ "version": "5.3.15",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -49,7 +49,6 @@ const REGISTRY_PING_PNG = Buffer.from(
49
49
  'base64'
50
50
  )
51
51
  const DEFAULT_REGISTRY_URL = 'https://beta.pinokio.co'
52
- const DEFAULT_REGISTRY_CONNECT_URL = `${DEFAULT_REGISTRY_URL}/connect/pinokio`
53
52
 
54
53
  const ex = fn => (req, res, next) => {
55
54
  Promise.resolve(fn(req, res, next)).catch(next);
@@ -1143,19 +1142,14 @@ class Server {
1143
1142
  }
1144
1143
  async getRegistryConfig() {
1145
1144
  let url = null
1146
- let apiKey = null
1147
1145
  try {
1148
1146
  const env = await Environment.get(this.kernel.homedir, this.kernel)
1149
1147
  if (env && env.PINOKIO_REGISTRY_URL) {
1150
1148
  const v = String(env.PINOKIO_REGISTRY_URL).trim()
1151
1149
  if (v) url = v
1152
1150
  }
1153
- if (env && env.PINOKIO_REGISTRY_API_KEY) {
1154
- const v = String(env.PINOKIO_REGISTRY_API_KEY).trim()
1155
- if (v) apiKey = v
1156
- }
1157
1151
  } catch (_) {}
1158
- if ((url == null || apiKey == null) && this.kernel && this.kernel.store) {
1152
+ if (url == null && this.kernel && this.kernel.store) {
1159
1153
  const registry = this.kernel.store.get('registry') || {}
1160
1154
  const updates = {}
1161
1155
  if (url == null && registry.url) {
@@ -1165,13 +1159,6 @@ class Server {
1165
1159
  updates.PINOKIO_REGISTRY_URL = v
1166
1160
  }
1167
1161
  }
1168
- if (apiKey == null && registry.apiKey) {
1169
- const v = String(registry.apiKey).trim()
1170
- if (v) {
1171
- apiKey = v
1172
- updates.PINOKIO_REGISTRY_API_KEY = v
1173
- }
1174
- }
1175
1162
  if (Object.keys(updates).length) {
1176
1163
  await this.updateEnvironmentVars(updates).catch(() => {})
1177
1164
  }
@@ -1179,67 +1166,15 @@ class Server {
1179
1166
  if (!url) {
1180
1167
  url = DEFAULT_REGISTRY_URL
1181
1168
  }
1182
- return { url, apiKey }
1183
- }
1184
- async getRegistryConnectUrl() {
1185
- let raw = null
1186
- let source = null
1187
- try {
1188
- const env = await Environment.get(this.kernel.homedir, this.kernel)
1189
- if (env && Object.prototype.hasOwnProperty.call(env, "PINOKIO_REGISTRY_CONNECT_URL")) {
1190
- raw = env.PINOKIO_REGISTRY_CONNECT_URL
1191
- source = "env-url"
1192
- } else if (env && Object.prototype.hasOwnProperty.call(env, "PINOKIO_REGISTRY_CONNECT_ENDPOINT")) {
1193
- raw = env.PINOKIO_REGISTRY_CONNECT_ENDPOINT
1194
- source = "env-endpoint"
1195
- }
1196
- } catch (_) {}
1197
- if (raw == null && process.env.PINOKIO_REGISTRY_CONNECT_URL != null) {
1198
- raw = process.env.PINOKIO_REGISTRY_CONNECT_URL
1199
- source = "process-url"
1200
- } else if (raw == null && process.env.PINOKIO_REGISTRY_CONNECT_ENDPOINT != null) {
1201
- raw = process.env.PINOKIO_REGISTRY_CONNECT_ENDPOINT
1202
- source = "process-endpoint"
1203
- }
1204
- let value = raw != null ? String(raw).trim() : ""
1205
- if (!value && this.kernel && this.kernel.store) {
1206
- const registry = this.kernel.store.get('registry') || {}
1207
- if (registry.connectUrl) {
1208
- value = String(registry.connectUrl).trim()
1209
- if (value) {
1210
- await this.updateEnvironmentVars({
1211
- PINOKIO_REGISTRY_CONNECT_URL: value,
1212
- PINOKIO_REGISTRY_CONNECT_ENDPOINT: ""
1213
- }).catch(() => {})
1214
- }
1215
- }
1216
- }
1217
- if (!value) {
1218
- const registry = await this.getRegistryConfig().catch(() => ({ url: null }))
1219
- const baseUrl = registry && registry.url ? String(registry.url).replace(/\/$/, "") : ""
1220
- value = baseUrl ? `${baseUrl}/connect/pinokio` : DEFAULT_REGISTRY_CONNECT_URL
1221
- }
1222
- if (value && source === "env-endpoint") {
1223
- await this.updateEnvironmentVars({
1224
- PINOKIO_REGISTRY_CONNECT_URL: value,
1225
- PINOKIO_REGISTRY_CONNECT_ENDPOINT: ""
1226
- }).catch(() => {})
1227
- }
1228
- return value ? value : null
1169
+ return { url, apiKey: null }
1229
1170
  }
1230
1171
  async getRegistryViewUrl(hash) {
1231
1172
  if (!hash) return null
1232
1173
  const registry = await this.getRegistryConfig().catch(() => ({ url: null, apiKey: null }))
1233
1174
  const baseUrlRaw = registry && registry.url ? String(registry.url) : ""
1234
1175
  const baseUrl = baseUrlRaw ? baseUrlRaw.replace(/\/$/, "") : ""
1235
- const connectUrl = await this.getRegistryConnectUrl().catch(() => null)
1236
1176
  let uiOrigin = null
1237
- if (connectUrl) {
1238
- try {
1239
- uiOrigin = new URL(connectUrl).origin
1240
- } catch (_) {}
1241
- }
1242
- if (!uiOrigin && baseUrlRaw) {
1177
+ if (baseUrlRaw) {
1243
1178
  try {
1244
1179
  uiOrigin = new URL(baseUrlRaw).origin
1245
1180
  } catch (_) {}
@@ -4836,7 +4771,6 @@ class Server {
4836
4771
  const successRaw = typeof req.query.success === 'string' ? req.query.success.trim() : ''
4837
4772
  const appSlug = typeof req.query.app === 'string' ? req.query.app.trim() : ''
4838
4773
  const registryRaw = typeof req.query.registry === 'string' ? req.query.registry.trim() : ''
4839
- const connectRaw = typeof req.query.connectUrl === 'string' ? req.query.connectUrl.trim() : ''
4840
4774
  const registryEnabled = await this.isRegistryEnabled().catch(() => false)
4841
4775
 
4842
4776
  let returnUrl = null
@@ -4881,9 +4815,7 @@ class Server {
4881
4815
  }
4882
4816
 
4883
4817
  const registryRawPresent = !!registryRaw
4884
- const connectRawPresent = !!connectRaw
4885
4818
  let registryOverride = normalizeHttpUrl(registryRaw)
4886
- let connectOverride = normalizeHttpUrl(connectRaw)
4887
4819
  if (returnUrl) {
4888
4820
  try {
4889
4821
  const returnHost = new URL(returnUrl)
@@ -4894,24 +4826,14 @@ class Server {
4894
4826
  || sameBaseDomain(regHost.hostname, returnHost.hostname)
4895
4827
  if (!ok) registryOverride = null
4896
4828
  }
4897
- if (connectOverride) {
4898
- const conHost = new URL(connectOverride)
4899
- const ok = hostMatches(conHost, returnHost)
4900
- || sameBaseDomain(conHost.hostname, returnHost.hostname)
4901
- if (!ok) connectOverride = null
4902
- }
4903
4829
  if (!registryOverride && !registryRawPresent && returnOrigin) {
4904
4830
  registryOverride = returnOrigin
4905
4831
  }
4906
- if (!connectOverride && !connectRawPresent && returnOrigin) {
4907
- connectOverride = `${returnOrigin.replace(/\/$/, '')}/connect/pinokio`
4908
- }
4909
4832
  } catch (_) {}
4910
4833
  } else {
4911
4834
  registryOverride = null
4912
- connectOverride = null
4913
4835
  }
4914
- console.log("[registry/checkin] repo=%s return=%s registryRaw=%s connectRaw=%s registryOverride=%s connectOverride=%s", repoUrl || "", returnUrl || "", registryRaw || "", connectRaw || "", registryOverride || "", connectOverride || "")
4836
+ console.log("[registry/checkin] repo=%s return=%s registryRaw=%s registryOverride=%s", repoUrl || "", returnUrl || "", registryRaw || "", registryOverride || "")
4915
4837
 
4916
4838
  let candidates = []
4917
4839
  if (repoUrl && this.kernel && this.kernel.git) {
@@ -4960,7 +4882,6 @@ class Server {
4960
4882
  successUrl,
4961
4883
  registryEnabled: !!registryEnabled,
4962
4884
  registryOverride,
4963
- connectOverride,
4964
4885
  candidates,
4965
4886
  }
4966
4887
  const dataJson = JSON.stringify(data).replace(/</g, '\\u003c')
@@ -4968,123 +4889,6 @@ class Server {
4968
4889
  res.render("registry_checkin", { returnUrl, dataJson })
4969
4890
  }))
4970
4891
 
4971
- // Registry linking endpoint
4972
- this.app.get("/registry/link", ex(async (req, res) => {
4973
- const { token, registry, app: appOrigin, next: nextRaw, pinokioNext: pinokioNextRaw } = req.query
4974
-
4975
- if (!token || !registry) {
4976
- res.status(400).render("registry_link", {
4977
- ok: false,
4978
- message: "Missing token or registry parameters",
4979
- registryUrl: registry ? String(registry) : null,
4980
- })
4981
- return
4982
- }
4983
-
4984
- const registryUrl = String(registry)
4985
- let connectUrl = null
4986
- let redirectUrl = null
4987
- try {
4988
- if (appOrigin) {
4989
- const u = new URL(String(appOrigin))
4990
- u.pathname = "/connect/pinokio"
4991
- u.search = ""
4992
- u.hash = ""
4993
- connectUrl = u.toString().replace(/\/$/, "")
4994
- }
4995
- } catch (_) {}
4996
-
4997
- if (connectUrl) {
4998
- try {
4999
- const u = new URL(connectUrl)
5000
- u.searchParams.set("linked", "1")
5001
- redirectUrl = u.toString()
5002
- } catch (_) {}
5003
- }
5004
-
5005
- if (appOrigin && nextRaw) {
5006
- try {
5007
- const base = new URL(String(appOrigin))
5008
- const nextVal = String(nextRaw)
5009
- const candidate = new URL(nextVal, base.toString())
5010
- if (candidate.origin === base.origin) redirectUrl = candidate.toString()
5011
- } catch (_) {}
5012
- }
5013
-
5014
- if (pinokioNextRaw) {
5015
- try {
5016
- const nextVal = String(pinokioNextRaw)
5017
- const protocol = (req.$source && req.$source.protocol) || req.get("X-Forwarded-Proto") || "http"
5018
- const host = (req.$source && req.$source.host) || req.get("host")
5019
- const base = new URL(`${protocol}://${host}`)
5020
- let candidate = null
5021
- if (nextVal.startsWith("/") && !nextVal.startsWith("//")) {
5022
- if (nextVal.startsWith("/registry/checkin")) {
5023
- candidate = new URL(nextVal, base.toString())
5024
- }
5025
- } else {
5026
- const parsed = new URL(nextVal)
5027
- if (parsed.origin === base.origin && parsed.pathname.startsWith("/registry/checkin")) {
5028
- candidate = parsed
5029
- }
5030
- }
5031
- if (candidate && candidate.origin === base.origin) redirectUrl = candidate.toString()
5032
- } catch (_) {}
5033
- }
5034
-
5035
- try {
5036
- // Exchange token for API key at the specified registry
5037
- const response = await axios.post(
5038
- `${registryUrl}/registry/exchange`,
5039
- { token: String(token) },
5040
- { headers: { 'Content-Type': 'application/json' } }
5041
- )
5042
-
5043
- const { apiKey, registryUrl: confirmedUrl } = response.data
5044
-
5045
- const envUpdates = {
5046
- PINOKIO_REGISTRY_API_KEY: apiKey,
5047
- PINOKIO_REGISTRY_URL: confirmedUrl || registryUrl
5048
- }
5049
- if (connectUrl) {
5050
- envUpdates.PINOKIO_REGISTRY_CONNECT_URL = connectUrl
5051
- envUpdates.PINOKIO_REGISTRY_CONNECT_ENDPOINT = ""
5052
- }
5053
- await this.updateEnvironmentVars(envUpdates)
5054
-
5055
- if (redirectUrl) {
5056
- res.redirect(redirectUrl)
5057
- return
5058
- }
5059
-
5060
- res.render("registry_link", { ok: true, registryUrl: confirmedUrl || registryUrl })
5061
- } catch (error) {
5062
- console.log("Error", error)
5063
- if (connectUrl) {
5064
- try {
5065
- const u = new URL(connectUrl)
5066
- u.searchParams.set("error", "1")
5067
- const msg = error && error.message ? String(error.message) : String(error)
5068
- u.searchParams.set("message", msg.slice(0, 240))
5069
- res.redirect(u.toString())
5070
- return
5071
- } catch (_) {}
5072
- }
5073
- res.status(500).render("registry_link", {
5074
- ok: false,
5075
- message: error && error.message ? error.message : String(error),
5076
- registryUrl,
5077
- })
5078
- }
5079
- }))
5080
-
5081
- this.app.get("/api/registry/status", ex(async (req, res) => {
5082
- const registry = await this.getRegistryConfig()
5083
- const baseUrl = registry && registry.url ? String(registry.url).replace(/\/$/, '') : null
5084
- const apiKey = registry && registry.apiKey ? String(registry.apiKey) : null
5085
- res.json({ linked: !!apiKey, url: baseUrl })
5086
- }))
5087
-
5088
4892
  this.app.post("/checkpoints/publish", ex(async (req, res) => {
5089
4893
  res.status(404).json({ ok: false, error: "Not found" })
5090
4894
  }))
@@ -5133,7 +4937,7 @@ class Server {
5133
4937
  }
5134
4938
  }
5135
4939
  const registryOverride = normalizeHttpUrl(typeof req.query.registry === 'string' ? req.query.registry.trim() : '')
5136
- const connectOverride = normalizeHttpUrl(typeof req.query.connectUrl === 'string' ? req.query.connectUrl.trim() : '')
4940
+ const registryToken = typeof req.get("x-registry-token") === "string" ? String(req.get("x-registry-token")).trim() : ""
5137
4941
  const registryEnabled = await this.isRegistryEnabled().catch(() => false)
5138
4942
  const publishRaw = typeof req.query.publish === 'string' ? req.query.publish.trim().toLowerCase() : ''
5139
4943
  const wantPublish = registryEnabled && (publishRaw === '1' || publishRaw === 'true' || publishRaw === 'cloud')
@@ -5150,19 +4954,10 @@ class Server {
5150
4954
  if (wantPublish) {
5151
4955
  const registry = await this.getRegistryConfig()
5152
4956
  const baseUrl = registryOverride || (registry && registry.url ? String(registry.url).replace(/\/$/, '') : null)
5153
- const apiKey = registry && registry.apiKey ? String(registry.apiKey) : null
5154
- let connectUrl = connectOverride
5155
- if (!connectUrl) {
5156
- if (registryOverride) {
5157
- connectUrl = `${registryOverride.replace(/\/$/, '')}/connect/pinokio`
5158
- } else {
5159
- connectUrl = await this.getRegistryConnectUrl().catch(() => null)
5160
- }
5161
- }
5162
- console.log("[checkpoints/snapshot] publish=1 registryOverride=%s baseUrl=%s connectUrl=%s hasApiKey=%s", registryOverride || "", baseUrl || "", connectUrl || "", apiKey ? "yes" : "no")
5163
- if (!baseUrl || !apiKey) {
5164
- await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "needs_link", at: Date.now() }).catch(() => {})
5165
- res.json({ ok: true, created: created || null, publish: { ok: false, code: "not_linked", connectUrl } })
4957
+ console.log("[checkpoints/snapshot] publish=1 registryOverride=%s baseUrl=%s hasToken=%s", registryOverride || "", baseUrl || "", registryToken ? "yes" : "no")
4958
+ if (!baseUrl || !registryToken) {
4959
+ await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "needs_token", at: Date.now() }).catch(() => {})
4960
+ res.json({ ok: true, created: created || null, publish: { ok: false, code: "missing_token" } })
5166
4961
  return
5167
4962
  }
5168
4963
  try {
@@ -5179,7 +4974,7 @@ class Server {
5179
4974
  checkpoint,
5180
4975
  system,
5181
4976
  },
5182
- { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` } }
4977
+ { headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${registryToken}` } }
5183
4978
  )
5184
4979
  const remoteId = response && response.data
5185
4980
  ? (response.data.checkpoint && response.data.checkpoint.id ? response.data.checkpoint.id : (response.data.id ? response.data.id : null))
@@ -5192,12 +4987,11 @@ class Server {
5192
4987
  } catch (error) {
5193
4988
  const status = error && error.response && error.response.status ? Number(error.response.status) : null
5194
4989
  const message = error && error.message ? error.message : String(error)
5195
- if (status === 401 || status === 403) {
5196
- await this.updateEnvironmentVars({ PINOKIO_REGISTRY_API_KEY: "" }).catch(() => {})
5197
- await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "needs_link", at: Date.now(), error: message }).catch(() => {})
5198
- res.json({ ok: true, created: created || null, publish: { ok: false, code: "not_linked", connectUrl } })
5199
- return
5200
- }
4990
+ if (status === 401 || status === 403) {
4991
+ await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "invalid_token", at: Date.now(), error: message }).catch(() => {})
4992
+ res.json({ ok: true, created: created || null, publish: { ok: false, code: "invalid_token" } })
4993
+ return
4994
+ }
5201
4995
  await this.kernel.git.setCheckpointSync(created.remoteKey, created.id, { status: "error", at: Date.now(), error: message }).catch(() => {})
5202
4996
  res.json({ ok: true, created: created || null, publish: { ok: false, code: "error", error: message } })
5203
4997
  return
@@ -9223,7 +9017,7 @@ class Server {
9223
9017
  title: name,
9224
9018
  url: gitRemote,
9225
9019
  //redirect_uri: "http://localhost:3001/apps/redirect?git=" + gitRemote,
9226
- redirect_uri: "https://pinokio.co/apps/redirect?git=" + gitRemote,
9020
+ redirect_uri: `${this.portal}/resolve?url=${encodeURIComponent(gitRemote)}`,
9227
9021
  platform: this.kernel.platform,
9228
9022
  theme: this.theme,
9229
9023
  agent: req.agent,
@@ -130,7 +130,6 @@
130
130
  <script>
131
131
  const data = <%- dataJson %>;
132
132
  const registryOverride = data.registryOverride || null;
133
- const connectOverride = data.connectOverride || null;
134
133
  const repoEl = document.getElementById('repo');
135
134
  const statusEl = document.getElementById('status');
136
135
  const pickerEl = document.getElementById('picker');
@@ -172,36 +171,25 @@
172
171
  return null;
173
172
  };
174
173
 
175
- const buildConnectUrl = (rawUrl) => {
176
- if (!rawUrl) return null;
174
+ const token = (() => {
175
+ const hash = window.location.hash ? window.location.hash.slice(1) : '';
176
+ if (!hash) return '';
177
+ const params = new URLSearchParams(hash);
178
+ return params.get('token') || '';
179
+ })();
180
+ if (token) {
177
181
  try {
178
- const u = new URL(rawUrl);
179
- if (u.protocol !== 'http:' && u.protocol !== 'https:') return null;
180
- const pinokioNext = `${window.location.pathname}${window.location.search}${window.location.hash || ''}`;
181
- if (pinokioNext.startsWith('/') && !pinokioNext.startsWith('//')) {
182
- u.searchParams.set('pinokioNext', pinokioNext);
183
- }
184
- if (data.returnUrl) {
185
- try {
186
- const ret = new URL(data.returnUrl);
187
- const nextPath = `${ret.pathname}${ret.search}${ret.hash || ''}`;
188
- if (nextPath.startsWith('/') && !nextPath.startsWith('//')) {
189
- u.searchParams.set('next', nextPath);
190
- }
191
- } catch (err) {}
192
- }
193
- u.searchParams.set('pinokioUrl', window.location.origin);
194
- return u.toString();
195
- } catch (err) {
196
- return null;
197
- }
198
- };
182
+ window.history.replaceState(null, '', window.location.pathname + window.location.search);
183
+ } catch (err) {}
184
+ }
199
185
 
200
186
  const candidates = Array.isArray(data.candidates) ? data.candidates : [];
201
187
  const repoLabel = data.repoUrl ? `Repo: ${data.repoUrl}` : 'Missing repo URL';
202
188
  if (repoEl) repoEl.textContent = repoLabel;
203
189
 
204
- if (!data.repoUrl) {
190
+ if (!token) {
191
+ setStatus('Missing check-in token. Return to the registry and try again.', 'error');
192
+ } else if (!data.repoUrl) {
205
193
  setStatus('Missing repo URL. Return to the registry and try again.', 'error');
206
194
  } else if (!data.returnUrl) {
207
195
  setStatus('Missing return URL. Return to the registry and try again.', 'error');
@@ -255,16 +243,19 @@
255
243
  setStatus('Missing folder selection.', 'error');
256
244
  return;
257
245
  }
246
+ if (!token) {
247
+ setStatus('Missing check-in token. Return to the registry and try again.', 'error');
248
+ return;
249
+ }
258
250
  setStatus('Creating checkpoint...', '');
259
251
  if (submitBtn) submitBtn.disabled = true;
260
252
  if (cancelBtn) cancelBtn.disabled = true;
261
253
  try {
262
254
  const qs = new URLSearchParams({ workspace: folder, publish: '1' });
263
255
  if (registryOverride) qs.set('registry', registryOverride);
264
- if (connectOverride) qs.set('connectUrl', connectOverride);
265
256
  const res = await fetch(`/checkpoints/snapshot?${qs.toString()}`, {
266
257
  method: 'POST',
267
- headers: { 'Accept': 'application/json' }
258
+ headers: { 'Accept': 'application/json', 'X-Registry-Token': token }
268
259
  });
269
260
  const payload = res.ok ? await res.json().catch(() => null) : null;
270
261
  if (!payload || !payload.ok) {
@@ -282,13 +273,12 @@
282
273
  redirectToReturn({ ok: '1', hash });
283
274
  return;
284
275
  }
285
- if (publish && publish.code === 'not_linked') {
286
- const connectUrl = buildConnectUrl(publish.connectUrl || '');
287
- if (connectUrl) {
288
- window.location.href = connectUrl;
289
- return;
290
- }
291
- redirectToReturn({ error: 'not_linked', connectUrl: publish.connectUrl || '' });
276
+ if (publish && publish.code === 'missing_token') {
277
+ redirectToReturn({ error: 'missing_token' });
278
+ return;
279
+ }
280
+ if (publish && publish.code === 'invalid_token') {
281
+ redirectToReturn({ error: 'invalid_token' });
292
282
  return;
293
283
  }
294
284
  if (publish && publish.code === 'error') {
@@ -1,153 +0,0 @@
1
- <!DOCTYPE html>
2
- <html>
3
- <head>
4
- <meta charset="UTF-8" />
5
- <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <style>
7
- body {
8
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
9
- display: flex;
10
- align-items: center;
11
- justify-content: center;
12
- min-height: 100vh;
13
- margin: 0;
14
- background: #f5f5f5;
15
- color: #0f172a;
16
- }
17
- .card {
18
- background: white;
19
- padding: 32px;
20
- border-radius: 12px;
21
- box-shadow: 0 2px 10px rgba(15, 23, 42, 0.08);
22
- max-width: 520px;
23
- width: calc(100% - 32px);
24
- }
25
- h1 {
26
- margin: 0 0 8px;
27
- font-size: 22px;
28
- }
29
- .subtitle {
30
- margin: 0 0 16px;
31
- color: #64748b;
32
- font-size: 14px;
33
- }
34
- label {
35
- display: block;
36
- font-weight: 600;
37
- font-size: 13px;
38
- margin-bottom: 6px;
39
- }
40
- input[type="url"] {
41
- width: 100%;
42
- padding: 10px 12px;
43
- border-radius: 8px;
44
- border: 1px solid #e2e8f0;
45
- font-size: 14px;
46
- box-sizing: border-box;
47
- }
48
- .hint {
49
- margin-top: 6px;
50
- font-size: 12px;
51
- color: #94a3b8;
52
- }
53
- .actions {
54
- margin-top: 16px;
55
- display: flex;
56
- gap: 10px;
57
- flex-wrap: wrap;
58
- }
59
- .btn {
60
- border: none;
61
- border-radius: 8px;
62
- padding: 10px 16px;
63
- font-size: 14px;
64
- cursor: pointer;
65
- text-decoration: none;
66
- display: inline-flex;
67
- align-items: center;
68
- justify-content: center;
69
- }
70
- .btn-primary {
71
- background: #2563eb;
72
- color: white;
73
- }
74
- .btn-secondary {
75
- background: #e2e8f0;
76
- color: #0f172a;
77
- }
78
- .message {
79
- margin-top: 12px;
80
- font-size: 13px;
81
- min-height: 18px;
82
- }
83
- .message.success {
84
- color: #166534;
85
- }
86
- .message.error {
87
- color: #991b1b;
88
- }
89
- </style>
90
- </head>
91
- <body>
92
- <div class="card">
93
- <h1>Registry Settings</h1>
94
- <div class="subtitle">Set the connect URL used for linking and published checkpoints.</div>
95
- <form id="registry-form">
96
- <label for="registry-connect-url">Registry connect URL</label>
97
- <input
98
- id="registry-connect-url"
99
- type="url"
100
- placeholder="https://your-registry/connect/pinokio"
101
- value="<%= registryUrlPrefill || registryConnectUrl || '' %>"
102
- autocomplete="off"
103
- />
104
- <div class="hint">Example: https://your-registry/connect/pinokio</div>
105
- <div class="actions">
106
- <button class="btn btn-primary" type="submit">Save</button>
107
- <a class="btn btn-secondary" href="/checkpoints">Back</a>
108
- </div>
109
- </form>
110
- <div id="registry-message" class="message" aria-live="polite"></div>
111
- </div>
112
-
113
- <script>
114
- const form = document.getElementById("registry-form");
115
- const input = document.getElementById("registry-connect-url");
116
- const message = document.getElementById("registry-message");
117
-
118
- const setMessage = (text, isError) => {
119
- if (!message) return;
120
- message.textContent = text || "";
121
- message.classList.toggle("error", !!isError);
122
- message.classList.toggle("success", !isError && !!text);
123
- };
124
-
125
- if (form) {
126
- form.addEventListener("submit", async (event) => {
127
- event.preventDefault();
128
- setMessage("");
129
- const connectUrl = input && input.value != null ? String(input.value).trim() : "";
130
- const submitBtn = form.querySelector("button[type='submit']");
131
- if (submitBtn) submitBtn.disabled = true;
132
- try {
133
- const res = await fetch("/checkpoints/registry", {
134
- method: "POST",
135
- headers: { "Content-Type": "application/json", "Accept": "application/json" },
136
- body: JSON.stringify({ connectUrl })
137
- });
138
- const payload = res.ok ? await res.json().catch(() => null) : null;
139
- if (!payload || !payload.ok) {
140
- setMessage("Could not save registry settings.", true);
141
- } else {
142
- setMessage("Saved.", false);
143
- }
144
- } catch (_) {
145
- setMessage("Could not save registry settings.", true);
146
- } finally {
147
- if (submitBtn) submitBtn.disabled = false;
148
- }
149
- });
150
- }
151
- </script>
152
- </body>
153
- </html>
@@ -1,76 +0,0 @@
1
- <html>
2
- <head>
3
- <meta charset="UTF-8" />
4
- <meta name="viewport" content="width=device-width, initial-scale=1" />
5
- <style>
6
- body {
7
- font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
8
- display: flex;
9
- align-items: center;
10
- justify-content: center;
11
- height: 100vh;
12
- margin: 0;
13
- background: #f5f5f5;
14
- }
15
- .card {
16
- background: white;
17
- padding: 40px;
18
- border-radius: 12px;
19
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
20
- text-align: center;
21
- max-width: 500px;
22
- }
23
- h1 {
24
- margin-top: 0;
25
- }
26
- h1.ok {
27
- color: #16a34a;
28
- }
29
- h1.error {
30
- color: #dc2626;
31
- }
32
- .meta {
33
- color: #666;
34
- font-size: 14px;
35
- margin-top: 20px;
36
- }
37
- .close {
38
- margin-top: 20px;
39
- padding: 10px 20px;
40
- background: #2563eb;
41
- color: white;
42
- border: none;
43
- border-radius: 6px;
44
- cursor: pointer;
45
- }
46
- .error {
47
- color: #666;
48
- font-size: 14px;
49
- }
50
- </style>
51
- </head>
52
- <body>
53
- <div class="card">
54
- <% if (ok) { %>
55
- <h1 class="ok">✓ Linked Successfully!</h1>
56
- <p>Pinokio is now connected to the registry.</p>
57
- <p>You can now back up checkpoints to the registry.</p>
58
- <div class="meta">Registry: <strong><%= registryUrl %></strong></div>
59
- <button class="close" onclick="window.close()">Close</button>
60
- <script>
61
- setTimeout(() => window.close(), 5000);
62
- </script>
63
- <% } else { %>
64
- <h1 class="error">❌ Linking Failed</h1>
65
- <% if (message) { %>
66
- <p class="error"><%= message %></p>
67
- <% } %>
68
- <% if (registryUrl) { %>
69
- <p class="error">Registry: <%= registryUrl %></p>
70
- <% } %>
71
- <a href="/">Go back to Pinokio</a>
72
- <% } %>
73
- </div>
74
- </body>
75
- </html>
76
-