pinokiod 7.1.48 → 7.1.49

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": "7.1.48",
3
+ "version": "7.1.49",
4
4
  "description": "",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/server/index.js CHANGED
@@ -7140,7 +7140,7 @@ class Server {
7140
7140
  return
7141
7141
  }
7142
7142
  const links = await taskWorkspaceLinks.listTaskWorkspaces(task.id, {
7143
- root: task.config.path,
7143
+ root: getTaskLaunchPath(task.config),
7144
7144
  pruneMissing: true
7145
7145
  })
7146
7146
  const items = links.workspaces.map((workspace) => {
@@ -7938,12 +7938,23 @@ class Server {
7938
7938
  }
7939
7939
  return `${baseName}-${Date.now()}`
7940
7940
  }
7941
- const getTaskLaunchRoot = (taskConfig) => {
7941
+ const normalizeTaskLaunchPath = (taskPath) => {
7942
+ const normalizedPath = typeof taskPath === "string" ? taskPath.trim() : ""
7943
+ return normalizedPath === "tasks" ? "workspaces" : normalizedPath
7944
+ }
7945
+ const getTaskLaunchPath = (taskConfig) => {
7942
7946
  const validatedTaskConfig = taskPackages.validateTaskConfig(taskConfig)
7943
- if (validatedTaskConfig.path === "workspaces") {
7947
+ return normalizeTaskLaunchPath(validatedTaskConfig.path)
7948
+ }
7949
+ const usesWorkspaceTaskPath = (taskConfig) => {
7950
+ return getTaskLaunchPath(taskConfig) === "workspaces"
7951
+ }
7952
+ const getTaskLaunchRoot = (taskConfig) => {
7953
+ const launchPath = getTaskLaunchPath(taskConfig)
7954
+ if (launchPath === "workspaces") {
7944
7955
  return path.resolve(getTerminalWorkspacesRoot())
7945
7956
  }
7946
- return path.resolve(this.kernel.path(validatedTaskConfig.path))
7957
+ return path.resolve(this.kernel.path(launchPath))
7947
7958
  }
7948
7959
  const extractTaskInputValuesFromPayload = (payload) => {
7949
7960
  const legacyValues = taskPackages.extractInputValues(payload)
@@ -8194,7 +8205,7 @@ class Server {
8194
8205
  const shareState = await buildTaskShareState(req, task)
8195
8206
  const taskUi = buildTaskPresentationState(task, shareState)
8196
8207
  const sidebarContext = await buildTaskSidebarContext()
8197
- const suggestedFolderName = task && task.config && task.config.path === "workspaces"
8208
+ const suggestedFolderName = task && task.config && usesWorkspaceTaskPath(task.config)
8198
8209
  ? ""
8199
8210
  : taskPackages.slugify(task && task.config ? task.config.title : task.id, task && task.id ? task.id : "task")
8200
8211
  const renderedPrompt = taskPackages.applyTemplateValues(task.template, promptValues)
@@ -9460,7 +9471,7 @@ class Server {
9460
9471
  const launchRoot = getTaskLaunchRoot(task.config)
9461
9472
  let folderName = folderNameInput
9462
9473
  if (!folderName) {
9463
- if (task.config.path === "workspaces") {
9474
+ if (usesWorkspaceTaskPath(task.config)) {
9464
9475
  folderName = await generateTerminalWorkspaceFolderName()
9465
9476
  } else {
9466
9477
  folderName = await suggestTaskFolderName(launchRoot, task.config.title)
@@ -9508,7 +9519,7 @@ class Server {
9508
9519
  await persistLauncherPromptContext(targetPath, {
9509
9520
  prompt,
9510
9521
  includeSpec: true,
9511
- includeRequest: task.config.path === "workspaces"
9522
+ includeRequest: usesWorkspaceTaskPath(task.config)
9512
9523
  })
9513
9524
  } catch (error) {
9514
9525
  await fs.promises.rm(targetPath, { recursive: true, force: true }).catch(() => {})
@@ -9841,7 +9852,7 @@ class Server {
9841
9852
 
9842
9853
  if (workspaceMode === "reuse") {
9843
9854
  const links = await taskWorkspaceLinks.listTaskWorkspaces(task.id, {
9844
- root: task.config.path,
9855
+ root: getTaskLaunchPath(task.config),
9845
9856
  pruneMissing: true
9846
9857
  })
9847
9858
  workspaceRef = requestedWorkspaceRef
@@ -9882,7 +9893,7 @@ class Server {
9882
9893
  const folderName = requestedWorkspaceName || await suggestTaskFolderName(launchRoot, task.config.title)
9883
9894
  targetPath = await createLauncherTargetFolder(launchRoot, folderName)
9884
9895
  createdTarget = true
9885
- workspaceRef = taskWorkspaceLinks.createWorkspaceRef(task.config.path, targetPath)
9896
+ workspaceRef = taskWorkspaceLinks.createWorkspaceRef(getTaskLaunchPath(task.config), targetPath)
9886
9897
  if (!workspaceRef) {
9887
9898
  throw new Error("Failed to create workspace link.")
9888
9899
  }
@@ -4418,4 +4418,109 @@ document.addEventListener("DOMContentLoaded", () => {
4418
4418
  }
4419
4419
  }
4420
4420
 
4421
+ function getTaskPendingCopyByAction(action) {
4422
+ if (action === '/task/install') {
4423
+ return {
4424
+ button: 'Installing...',
4425
+ status: 'Installing the task and reopening this page.'
4426
+ };
4427
+ }
4428
+ if (action === '/task/start') {
4429
+ return {
4430
+ button: 'Launching...',
4431
+ status: 'Preparing the workspace and opening your selected tool.'
4432
+ };
4433
+ }
4434
+ return {
4435
+ button: 'Working...',
4436
+ status: 'Finishing your request.'
4437
+ };
4438
+ }
4439
+
4440
+ function ensureTaskSubmitFeedback(form) {
4441
+ let feedback = form.querySelector('[data-task-submit-feedback]');
4442
+ if (feedback) {
4443
+ return feedback;
4444
+ }
4445
+ feedback = document.createElement('p');
4446
+ feedback.className = 'task-submit-feedback';
4447
+ feedback.setAttribute('data-task-submit-feedback', '');
4448
+ feedback.setAttribute('aria-live', 'polite');
4449
+ feedback.setAttribute('aria-hidden', 'true');
4450
+
4451
+ const feedbackText = document.createElement('span');
4452
+ feedbackText.setAttribute('data-task-submit-feedback-text', '');
4453
+ feedback.appendChild(feedbackText);
4454
+ form.appendChild(feedback);
4455
+ return feedback;
4456
+ }
4457
+
4458
+ function initTaskPendingSubmitFallback() {
4459
+ const forms = Array.from(document.querySelectorAll('form[action="/task/install"], form[action="/task/start"]'));
4460
+ forms.forEach((form) => {
4461
+ if (form.matches('[data-task-pending-form]') || form.dataset.taskPendingBound === 'true') {
4462
+ return;
4463
+ }
4464
+
4465
+ const action = form.getAttribute('action') || '';
4466
+ form.dataset.taskPendingBound = 'true';
4467
+
4468
+ form.addEventListener('submit', (event) => {
4469
+ if (form.dataset.taskSubmitting === 'true') {
4470
+ event.preventDefault();
4471
+ return;
4472
+ }
4473
+
4474
+ const copy = getTaskPendingCopyByAction(action);
4475
+ const submitter = event.submitter && event.submitter.form === form
4476
+ ? event.submitter
4477
+ : form.querySelector('button[type="submit"], input[type="submit"]');
4478
+ const feedback = ensureTaskSubmitFeedback(form);
4479
+ const feedbackText = feedback.querySelector('[data-task-submit-feedback-text]') || feedback;
4480
+
4481
+ event.preventDefault();
4482
+ form.dataset.taskSubmitting = 'true';
4483
+ form.classList.add('is-submitting');
4484
+ form.setAttribute('aria-busy', 'true');
4485
+ document.body.classList.add('task-page-busy');
4486
+
4487
+ if (submitter) {
4488
+ submitter.classList.add('is-busy');
4489
+ submitter.setAttribute('aria-disabled', 'true');
4490
+ if (submitter.tagName === 'BUTTON') {
4491
+ const label = submitter.querySelector('span');
4492
+ if (label) {
4493
+ label.textContent = copy.button;
4494
+ } else {
4495
+ submitter.textContent = copy.button;
4496
+ }
4497
+ } else if (submitter.tagName === 'INPUT') {
4498
+ submitter.value = copy.button;
4499
+ }
4500
+ submitter.disabled = true;
4501
+ }
4502
+
4503
+ Array.from(form.querySelectorAll('button[type="submit"], input[type="submit"]')).forEach((button) => {
4504
+ if (button === submitter) {
4505
+ return;
4506
+ }
4507
+ button.disabled = true;
4508
+ button.setAttribute('aria-disabled', 'true');
4509
+ });
4510
+
4511
+ feedbackText.textContent = copy.status;
4512
+ feedback.classList.add('is-visible');
4513
+ feedback.setAttribute('aria-hidden', 'false');
4514
+
4515
+ window.requestAnimationFrame(() => {
4516
+ window.setTimeout(() => {
4517
+ form.submit();
4518
+ }, 0);
4519
+ });
4520
+ });
4521
+ });
4522
+ }
4523
+
4524
+ initTaskPendingSubmitFallback();
4525
+
4421
4526
  })
@@ -1439,6 +1439,28 @@ body.dark .task-badge-warning {
1439
1439
  transition: background 140ms ease, border-color 140ms ease, color 140ms ease;
1440
1440
  }
1441
1441
 
1442
+ .task-button.is-busy,
1443
+ .task-link-button.is-busy {
1444
+ pointer-events: none;
1445
+ }
1446
+
1447
+ .task-button.is-busy > i,
1448
+ .task-link-button.is-busy > i {
1449
+ display: none;
1450
+ }
1451
+
1452
+ .task-button.is-busy::before,
1453
+ .task-link-button.is-busy::before {
1454
+ content: "";
1455
+ width: 11px;
1456
+ height: 11px;
1457
+ flex: 0 0 auto;
1458
+ border-radius: 999px;
1459
+ border: 1.5px solid currentColor;
1460
+ border-right-color: transparent;
1461
+ animation: task-button-spin 720ms linear infinite;
1462
+ }
1463
+
1442
1464
  .task-button:hover,
1443
1465
  .task-link-button:hover {
1444
1466
  background: var(--task-soft);
@@ -1539,6 +1561,29 @@ body.dark .task-link-button.danger {
1539
1561
  margin: 0;
1540
1562
  }
1541
1563
 
1564
+ .task-submit-feedback {
1565
+ margin: 0;
1566
+ display: grid;
1567
+ grid-template-rows: 0fr;
1568
+ opacity: 0;
1569
+ color: var(--task-muted);
1570
+ font-size: 12px;
1571
+ line-height: 1.45;
1572
+ transition:
1573
+ grid-template-rows 220ms cubic-bezier(0.25, 1, 0.5, 1),
1574
+ opacity 180ms cubic-bezier(0.25, 1, 0.5, 1);
1575
+ }
1576
+
1577
+ .task-submit-feedback > span {
1578
+ overflow: hidden;
1579
+ min-height: 0;
1580
+ }
1581
+
1582
+ .task-submit-feedback.is-visible {
1583
+ grid-template-rows: 1fr;
1584
+ opacity: 1;
1585
+ }
1586
+
1542
1587
  .task-button.subtle {
1543
1588
  color: var(--task-muted);
1544
1589
  }
@@ -1569,6 +1614,23 @@ body.dark .task-link-button.danger {
1569
1614
  margin-top: 18px;
1570
1615
  }
1571
1616
 
1617
+ .task-install-actions .task-submit-feedback {
1618
+ flex-basis: 100%;
1619
+ }
1620
+
1621
+ .task-install-actions.is-submitting .task-link-button {
1622
+ pointer-events: none;
1623
+ opacity: 0.55;
1624
+ }
1625
+
1626
+ .task-prompt-actions .task-submit-feedback {
1627
+ margin-top: 2px;
1628
+ }
1629
+
1630
+ .task-run-form.is-submitting .task-prompt-code {
1631
+ opacity: 0.82;
1632
+ }
1633
+
1572
1634
  .task-section-inline {
1573
1635
  padding-top: 12px;
1574
1636
  padding-bottom: 12px;
@@ -1673,6 +1735,21 @@ body.dark .task-status.error {
1673
1735
  opacity: 0.5;
1674
1736
  }
1675
1737
 
1738
+ body.task-page-busy {
1739
+ cursor: progress;
1740
+ }
1741
+
1742
+ body.task-page-busy .task-button,
1743
+ body.task-page-busy .task-link-button {
1744
+ cursor: progress;
1745
+ }
1746
+
1747
+ @keyframes task-button-spin {
1748
+ to {
1749
+ transform: rotate(360deg);
1750
+ }
1751
+ }
1752
+
1676
1753
  .task-share-section {
1677
1754
  display: grid;
1678
1755
  gap: 8px;
@@ -2783,3 +2860,18 @@ body.dark .plugin-option .option-icon {
2783
2860
  justify-self: end;
2784
2861
  }
2785
2862
  }
2863
+
2864
+ @media (prefers-reduced-motion: reduce) {
2865
+ .task-button,
2866
+ .task-link-button,
2867
+ .task-submit-feedback {
2868
+ transition: none;
2869
+ }
2870
+
2871
+ .task-button.is-busy::before,
2872
+ .task-link-button.is-busy::before {
2873
+ animation: none;
2874
+ border-right-color: currentColor;
2875
+ opacity: 0.72;
2876
+ }
2877
+ }
@@ -748,6 +748,97 @@
748
748
  });
749
749
  }
750
750
 
751
+ function getTaskPendingCopy(kind) {
752
+ if (kind === "install") {
753
+ return {
754
+ button: "Installing...",
755
+ status: "Installing the task and reopening this page."
756
+ };
757
+ }
758
+ if (kind === "run") {
759
+ return {
760
+ button: "Launching...",
761
+ status: "Preparing the workspace and opening your selected tool."
762
+ };
763
+ }
764
+ return {
765
+ button: "Working...",
766
+ status: "Finishing your request."
767
+ };
768
+ }
769
+
770
+ function setTaskPendingState(form, submitter, kind) {
771
+ const copy = getTaskPendingCopy(kind);
772
+ const primarySubmitter = submitter && submitter.form === form
773
+ ? submitter
774
+ : form.querySelector("button[type='submit'], input[type='submit']");
775
+ const feedback = form.querySelector("[data-task-submit-feedback]");
776
+ const feedbackText = feedback
777
+ ? (feedback.querySelector("[data-task-submit-feedback-text]") || feedback)
778
+ : null;
779
+
780
+ form.classList.add("is-submitting");
781
+ form.setAttribute("aria-busy", "true");
782
+ document.body.classList.add("task-page-busy");
783
+
784
+ if (primarySubmitter) {
785
+ primarySubmitter.classList.add("is-busy");
786
+ primarySubmitter.setAttribute("aria-disabled", "true");
787
+ if (primarySubmitter.tagName === "BUTTON") {
788
+ const label = primarySubmitter.querySelector("span");
789
+ if (label) {
790
+ label.textContent = copy.button;
791
+ } else {
792
+ primarySubmitter.textContent = copy.button;
793
+ }
794
+ } else if (primarySubmitter.tagName === "INPUT") {
795
+ primarySubmitter.value = copy.button;
796
+ }
797
+ primarySubmitter.disabled = true;
798
+ }
799
+
800
+ Array.from(form.querySelectorAll("button[type='submit'], input[type='submit']")).forEach((button) => {
801
+ if (button === primarySubmitter) {
802
+ return;
803
+ }
804
+ button.disabled = true;
805
+ button.setAttribute("aria-disabled", "true");
806
+ });
807
+
808
+ if (feedback && feedbackText) {
809
+ feedbackText.textContent = copy.status;
810
+ feedback.classList.add("is-visible");
811
+ feedback.setAttribute("aria-hidden", "false");
812
+ }
813
+ }
814
+
815
+ function initTaskPendingForms() {
816
+ const forms = Array.from(document.querySelectorAll("form[data-task-pending-form]"));
817
+ forms.forEach((form) => {
818
+ form.addEventListener("submit", (event) => {
819
+ if (form.dataset.taskSubmitting === "true") {
820
+ event.preventDefault();
821
+ return;
822
+ }
823
+
824
+ const kind = form.getAttribute("data-task-pending-form") || "";
825
+ const submitter = event.submitter && event.submitter.form === form
826
+ ? event.submitter
827
+ : form.querySelector("button[type='submit'], input[type='submit']");
828
+
829
+ event.preventDefault();
830
+ form.dataset.taskSubmitting = "true";
831
+ setTaskPendingState(form, submitter, kind);
832
+
833
+ window.requestAnimationFrame(() => {
834
+ window.setTimeout(() => {
835
+ form.submit();
836
+ }, 0);
837
+ });
838
+ });
839
+ });
840
+ }
841
+
751
842
  let taskLauncherBooted = false;
752
843
 
753
844
  function bootTaskLauncher() {
@@ -759,6 +850,7 @@
759
850
  initTaskBuilder();
760
851
  initTaskLibraryPage();
761
852
  initTaskConfirmForms();
853
+ initTaskPendingForms();
762
854
  }
763
855
 
764
856
  if (document.readyState === "loading") {
@@ -391,6 +391,17 @@
391
391
  return 'workspaces';
392
392
  }
393
393
 
394
+ function normalizeTaskSelectionPath(taskPath) {
395
+ return taskPath === 'tasks' ? 'workspaces' : taskPath;
396
+ }
397
+
398
+ function taskMatchesSelectionPath(task, currentPath) {
399
+ if (!task) {
400
+ return false;
401
+ }
402
+ return normalizeTaskSelectionPath(task.path || '') === (currentPath || '');
403
+ }
404
+
394
405
  function getIntentNameRelativePath(intent) {
395
406
  const normalizedIntent = normalizeIntent(intent);
396
407
  if (normalizedIntent === 'create_app') return 'api';
@@ -2108,7 +2119,7 @@
2108
2119
  function getVisibleTasks() {
2109
2120
  const query = state.query.toLowerCase();
2110
2121
  return state.allTasks.filter((task) => {
2111
- if (!task || task.path !== state.currentPath) {
2122
+ if (!taskMatchesSelectionPath(task, state.currentPath)) {
2112
2123
  return false;
2113
2124
  }
2114
2125
  if (!query) {
@@ -2177,7 +2188,7 @@
2177
2188
  function render() {
2178
2189
  const visibleTasks = getVisibleTasks();
2179
2190
  const selectedTask = getSelectedTask();
2180
- const hasAvailableTasks = state.allTasks.some((task) => task && task.path === state.currentPath);
2191
+ const hasAvailableTasks = state.allTasks.some((task) => taskMatchesSelectionPath(task, state.currentPath));
2181
2192
  const showingDetails = Boolean(selectedTask);
2182
2193
  const hasVisibleTasks = visibleTasks.length > 0;
2183
2194
  toggle.disabled = false;
@@ -2366,7 +2377,7 @@
2366
2377
  setTasks(tasks) {
2367
2378
  state.allTasks = Array.isArray(tasks) ? tasks.slice() : [];
2368
2379
  const selectedTask = getSelectedTask();
2369
- if (selectedTask && selectedTask.path !== state.currentPath) {
2380
+ if (!taskMatchesSelectionPath(selectedTask, state.currentPath)) {
2370
2381
  state.selectedTaskId = '';
2371
2382
  }
2372
2383
  render();
@@ -2374,7 +2385,7 @@
2374
2385
  setPath(taskPath) {
2375
2386
  state.currentPath = taskPath || 'workspaces';
2376
2387
  const selectedTask = getSelectedTask();
2377
- if (selectedTask && selectedTask.path !== state.currentPath) {
2388
+ if (!taskMatchesSelectionPath(selectedTask, state.currentPath)) {
2378
2389
  state.selectedTaskId = '';
2379
2390
  }
2380
2391
  state.query = '';
@@ -2547,7 +2558,7 @@
2547
2558
 
2548
2559
  function getTaskSource() {
2549
2560
  return state.tasks
2550
- .filter((task) => task && task.path === state.currentPath)
2561
+ .filter((task) => taskMatchesSelectionPath(task, state.currentPath))
2551
2562
  .slice()
2552
2563
  .sort(compareTasksByBrowseOrder);
2553
2564
  }
@@ -3373,7 +3384,7 @@
3373
3384
  setPath(taskPath) {
3374
3385
  const nextPath = taskPath || 'workspaces';
3375
3386
  const task = getSelectedTask();
3376
- const selectedTaskInvalid = Boolean(task && task.path !== nextPath);
3387
+ const selectedTaskInvalid = Boolean(task && !taskMatchesSelectionPath(task, nextPath));
3377
3388
  if (state.currentPath === nextPath && !selectedTaskInvalid) {
3378
3389
  return;
3379
3390
  }
@@ -3386,7 +3397,7 @@
3386
3397
  setTasks(tasks) {
3387
3398
  state.tasks = Array.isArray(tasks) ? tasks.slice() : [];
3388
3399
  const task = getSelectedTask();
3389
- if (task && task.path !== state.currentPath) {
3400
+ if (!taskMatchesSelectionPath(task, state.currentPath)) {
3390
3401
  state.selectedTaskId = '';
3391
3402
  }
3392
3403
  render();
@@ -13,6 +13,7 @@
13
13
  <% if (agent === "electron") { %>
14
14
  <link href="/electron.css" rel="stylesheet"/>
15
15
  <% } %>
16
+ <script src="/task-launcher.js" defer></script>
16
17
  </head>
17
18
  <body class="<%= theme %> task-launcher-page task-page task-install-page" data-agent="<%= agent %>">
18
19
  <%- include('partials/app_navheader', { agent }) %>
@@ -63,7 +64,7 @@
63
64
  </section>
64
65
  <% } %>
65
66
 
66
- <form class="task-install-actions" method="post" action="/task/install">
67
+ <form class="task-install-actions" method="post" action="/task/install" data-task-pending-form="install">
67
68
  <input type="hidden" name="ref" value="<%= ref %>">
68
69
  <input type="hidden" name="returnTo" value="<%= returnTo %>">
69
70
  <button class="task-button primary" type="submit">
@@ -71,6 +72,9 @@
71
72
  <span>Install and continue</span>
72
73
  </button>
73
74
  <a class="task-link-button" href="/tasks">Cancel</a>
75
+ <p class="task-submit-feedback" data-task-submit-feedback aria-live="polite" aria-hidden="true">
76
+ <span data-task-submit-feedback-text></span>
77
+ </p>
74
78
  </form>
75
79
  </div>
76
80
  </section>
@@ -17,12 +17,13 @@
17
17
  <script src="/task-share.js" defer></script>
18
18
  </head>
19
19
  <%
20
- const runRootCopy = task.config.path === 'api'
20
+ const effectiveTaskPath = task.config.path === 'tasks' ? 'workspaces' : task.config.path
21
+ const runRootCopy = effectiveTaskPath === 'api'
21
22
  ? 'Creates a new app.'
22
- : task.config.path === 'plugin'
23
+ : effectiveTaskPath === 'plugin'
23
24
  ? 'Creates a new plugin.'
24
25
  : 'Runs in a new workspace.'
25
- const folderMeta = task.config.path === 'workspaces' ? 'Optional' : ''
26
+ const folderMeta = effectiveTaskPath === 'workspaces' ? 'Optional' : ''
26
27
  const headerDescription = task.config.description && !/{{/.test(task.config.description)
27
28
  ? task.config.description
28
29
  : ''
@@ -114,7 +115,7 @@
114
115
  <div class="task-status error"><%= error %></div>
115
116
  <% } %>
116
117
 
117
- <form class="task-run-form task-detail-layout" method="post" action="/task/start">
118
+ <form class="task-run-form task-detail-layout" method="post" action="/task/start" data-task-pending-form="run">
118
119
  <input type="hidden" name="id" value="<%= task.id %>">
119
120
 
120
121
  <div class="task-detail-main">
@@ -126,6 +127,9 @@
126
127
  <span>Run</span>
127
128
  </button>
128
129
  <p class="task-inline-help">Tool and folder stay local. The permalink tracks only the task inputs.</p>
130
+ <p class="task-submit-feedback" data-task-submit-feedback aria-live="polite" aria-hidden="true">
131
+ <span data-task-submit-feedback-text></span>
132
+ </p>
129
133
  </div>
130
134
  </section>
131
135
  </div>