@njeybe/nativ 2.2.0 → 2.3.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 (141) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +15 -0
  3. package/dist/runner/agent-supervisor.d.ts +5 -3
  4. package/dist/runner/agent-supervisor.js +17 -12
  5. package/dist/server/studio-server.js +1 -130
  6. package/dist/server/studio-ui/document.d.ts +3 -0
  7. package/dist/server/studio-ui/document.js +22 -0
  8. package/dist/server/studio-ui/pages/benchmarks/index.d.ts +2 -0
  9. package/dist/server/studio-ui/pages/benchmarks/index.js +11 -0
  10. package/dist/server/studio-ui/pages/benchmarks/script.d.ts +1 -0
  11. package/dist/server/studio-ui/pages/benchmarks/script.js +104 -0
  12. package/dist/server/studio-ui/pages/benchmarks/styles.d.ts +1 -0
  13. package/dist/server/studio-ui/pages/benchmarks/styles.js +30 -0
  14. package/dist/server/studio-ui/pages/database/browser.d.ts +1 -0
  15. package/dist/server/studio-ui/pages/database/browser.js +326 -0
  16. package/dist/server/studio-ui/pages/database/connection.d.ts +1 -0
  17. package/dist/server/studio-ui/pages/database/connection.js +158 -0
  18. package/dist/server/studio-ui/pages/database/data-shell.d.ts +1 -0
  19. package/dist/server/studio-ui/pages/database/data-shell.js +165 -0
  20. package/dist/server/studio-ui/pages/database/drift.d.ts +1 -0
  21. package/dist/server/studio-ui/pages/database/drift.js +301 -0
  22. package/dist/server/studio-ui/pages/database/events.d.ts +1 -0
  23. package/dist/server/studio-ui/pages/database/events.js +231 -0
  24. package/dist/server/studio-ui/pages/database/explorer.d.ts +1 -0
  25. package/dist/server/studio-ui/pages/database/explorer.js +183 -0
  26. package/dist/server/studio-ui/pages/database/index.d.ts +2 -0
  27. package/dist/server/studio-ui/pages/database/index.js +28 -0
  28. package/dist/server/studio-ui/pages/database/markup.d.ts +1 -0
  29. package/dist/server/studio-ui/pages/database/markup.js +32 -0
  30. package/dist/server/studio-ui/pages/database/state.d.ts +2 -0
  31. package/dist/server/studio-ui/pages/database/state.js +57 -0
  32. package/dist/server/studio-ui/pages/database/styles.d.ts +1 -0
  33. package/dist/server/studio-ui/pages/database/styles.js +118 -0
  34. package/dist/server/studio-ui/pages/flow/index.d.ts +2 -0
  35. package/dist/server/studio-ui/pages/flow/index.js +11 -0
  36. package/dist/server/studio-ui/pages/flow/layout.d.ts +1 -0
  37. package/dist/server/studio-ui/pages/flow/layout.js +105 -0
  38. package/dist/server/studio-ui/pages/flow/plan-view.d.ts +1 -0
  39. package/dist/server/studio-ui/pages/flow/plan-view.js +67 -0
  40. package/dist/server/studio-ui/pages/flow/script.d.ts +1 -0
  41. package/dist/server/studio-ui/pages/flow/script.js +61 -0
  42. package/dist/server/studio-ui/pages/flow/styles.d.ts +1 -0
  43. package/dist/server/studio-ui/pages/flow/styles.js +59 -0
  44. package/dist/server/studio-ui/pages/flow/timeline-model.d.ts +1 -0
  45. package/dist/server/studio-ui/pages/flow/timeline-model.js +95 -0
  46. package/dist/server/studio-ui/pages/flow/timeline-view.d.ts +1 -0
  47. package/dist/server/studio-ui/pages/flow/timeline-view.js +92 -0
  48. package/dist/server/studio-ui/pages/home/index.d.ts +2 -0
  49. package/dist/server/studio-ui/pages/home/index.js +11 -0
  50. package/dist/server/studio-ui/pages/home/script.d.ts +1 -0
  51. package/dist/server/studio-ui/pages/home/script.js +160 -0
  52. package/dist/server/studio-ui/pages/home/styles.d.ts +1 -0
  53. package/dist/server/studio-ui/pages/home/styles.js +48 -0
  54. package/dist/server/studio-ui/pages/index.d.ts +2 -0
  55. package/dist/server/studio-ui/pages/index.js +17 -0
  56. package/dist/server/studio-ui/pages/tasks/index.d.ts +2 -0
  57. package/dist/server/studio-ui/pages/tasks/index.js +11 -0
  58. package/dist/server/studio-ui/pages/tasks/script.d.ts +1 -0
  59. package/dist/server/studio-ui/pages/tasks/script.js +96 -0
  60. package/dist/server/studio-ui/pages/tasks/styles.d.ts +1 -0
  61. package/dist/server/studio-ui/pages/tasks/styles.js +37 -0
  62. package/dist/server/studio-ui/pages/team/index.d.ts +2 -0
  63. package/dist/server/studio-ui/pages/team/index.js +11 -0
  64. package/dist/server/studio-ui/pages/team/script.d.ts +1 -0
  65. package/dist/server/studio-ui/pages/team/script.js +112 -0
  66. package/dist/server/studio-ui/pages/team/styles.d.ts +1 -0
  67. package/dist/server/studio-ui/pages/team/styles.js +22 -0
  68. package/dist/server/studio-ui/pages/types.d.ts +13 -0
  69. package/dist/server/studio-ui/pages/types.js +1 -0
  70. package/dist/server/studio-ui/pages/worktrees/index.d.ts +2 -0
  71. package/dist/server/studio-ui/pages/worktrees/index.js +11 -0
  72. package/dist/server/studio-ui/pages/worktrees/script.d.ts +1 -0
  73. package/dist/server/studio-ui/pages/worktrees/script.js +104 -0
  74. package/dist/server/studio-ui/pages/worktrees/styles.d.ts +1 -0
  75. package/dist/server/studio-ui/pages/worktrees/styles.js +15 -0
  76. package/dist/server/studio-ui/shell/actions.d.ts +1 -0
  77. package/dist/server/studio-ui/shell/actions.js +49 -0
  78. package/dist/server/studio-ui/shell/boot.d.ts +2 -0
  79. package/dist/server/studio-ui/shell/boot.js +13 -0
  80. package/dist/server/studio-ui/shell/console.d.ts +1 -0
  81. package/dist/server/studio-ui/shell/console.js +194 -0
  82. package/dist/server/studio-ui/shell/core.d.ts +1 -0
  83. package/dist/server/studio-ui/shell/core.js +50 -0
  84. package/dist/server/studio-ui/shell/detail-view.d.ts +1 -0
  85. package/dist/server/studio-ui/shell/detail-view.js +80 -0
  86. package/dist/server/studio-ui/shell/detail.d.ts +1 -0
  87. package/dist/server/studio-ui/shell/detail.js +83 -0
  88. package/dist/server/studio-ui/shell/dispatch.d.ts +1 -0
  89. package/dist/server/studio-ui/shell/dispatch.js +150 -0
  90. package/dist/server/studio-ui/shell/events-tools.d.ts +1 -0
  91. package/dist/server/studio-ui/shell/events-tools.js +94 -0
  92. package/dist/server/studio-ui/shell/format.d.ts +1 -0
  93. package/dist/server/studio-ui/shell/format.js +151 -0
  94. package/dist/server/studio-ui/shell/icons.d.ts +2 -0
  95. package/dist/server/studio-ui/shell/icons.js +20 -0
  96. package/dist/server/studio-ui/shell/markup-head.d.ts +1 -0
  97. package/dist/server/studio-ui/shell/markup-head.js +43 -0
  98. package/dist/server/studio-ui/shell/markup-panels.d.ts +2 -0
  99. package/dist/server/studio-ui/shell/markup-panels.js +7 -0
  100. package/dist/server/studio-ui/shell/markup-tools.d.ts +1 -0
  101. package/dist/server/studio-ui/shell/markup-tools.js +222 -0
  102. package/dist/server/studio-ui/shell/model.d.ts +1 -0
  103. package/dist/server/studio-ui/shell/model.js +192 -0
  104. package/dist/server/studio-ui/shell/paint.d.ts +1 -0
  105. package/dist/server/studio-ui/shell/paint.js +154 -0
  106. package/dist/server/studio-ui/shell/palette-view.d.ts +1 -0
  107. package/dist/server/studio-ui/shell/palette-view.js +60 -0
  108. package/dist/server/studio-ui/shell/palette.d.ts +1 -0
  109. package/dist/server/studio-ui/shell/palette.js +80 -0
  110. package/dist/server/studio-ui/shell/pipeline-state.d.ts +1 -0
  111. package/dist/server/studio-ui/shell/pipeline-state.js +152 -0
  112. package/dist/server/studio-ui/shell/proposals.d.ts +1 -0
  113. package/dist/server/studio-ui/shell/proposals.js +197 -0
  114. package/dist/server/studio-ui/shell/pure.d.ts +3 -0
  115. package/dist/server/studio-ui/shell/pure.js +9 -0
  116. package/dist/server/studio-ui/shell/router.d.ts +1 -0
  117. package/dist/server/studio-ui/shell/router.js +95 -0
  118. package/dist/server/studio-ui/shell/sidebar-view.d.ts +1 -0
  119. package/dist/server/studio-ui/shell/sidebar-view.js +38 -0
  120. package/dist/server/studio-ui/shell/sync.d.ts +1 -0
  121. package/dist/server/studio-ui/shell/sync.js +120 -0
  122. package/dist/server/studio-ui/shell/toasts-format.d.ts +1 -0
  123. package/dist/server/studio-ui/shell/toasts-format.js +89 -0
  124. package/dist/server/studio-ui/shell/triage.d.ts +1 -0
  125. package/dist/server/studio-ui/shell/triage.js +210 -0
  126. package/dist/server/studio-ui/styles/console.d.ts +1 -0
  127. package/dist/server/studio-ui/styles/console.js +83 -0
  128. package/dist/server/studio-ui/styles/dialogs.d.ts +1 -0
  129. package/dist/server/studio-ui/styles/dialogs.js +118 -0
  130. package/dist/server/studio-ui/styles/responsive.d.ts +1 -0
  131. package/dist/server/studio-ui/styles/responsive.js +28 -0
  132. package/dist/server/studio-ui/styles/shell.d.ts +1 -0
  133. package/dist/server/studio-ui/styles/shell.js +117 -0
  134. package/dist/server/studio-ui/styles/strategist.d.ts +1 -0
  135. package/dist/server/studio-ui/styles/strategist.js +77 -0
  136. package/dist/server/studio-ui/styles/tokens-base.d.ts +1 -0
  137. package/dist/server/studio-ui/styles/tokens-base.js +131 -0
  138. package/dist/server/studio-ui.d.ts +1 -10
  139. package/dist/server/studio-ui.js +63 -5314
  140. package/package.json +2 -2
  141. package/plugin/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1,192 @@
1
+ // Pure client model: joins plan, telemetry, runs and escalations into one object per refresh.
2
+ export const modelScript = String.raw ` // ─── Studio client model: buildModel(api) is pure ──────────────────────────
3
+ var RUN_ACTIVE = { spawning_worktree: 1, running: 1, verifying: 1, merging: 1 };
4
+ function isRunActive(run) { return !!run && !!RUN_ACTIVE[run.status]; }
5
+
6
+ /** Text before the first colon, at most 46 characters. */
7
+ function shortName(name) {
8
+ var head = String(name || '').split(':')[0].replace(/^\s+|\s+$/g, '');
9
+ if (head.length <= 46) return head;
10
+ return head.slice(0, 44).replace(/[\s,&]+\S*$/, '') + '…';
11
+ }
12
+
13
+ function toTime(value) {
14
+ var t = value ? Date.parse(value) : NaN;
15
+ return isNaN(t) ? null : t;
16
+ }
17
+
18
+ function positive(n) { return typeof n === 'number' && isFinite(n) && n > 0 ? n : null; }
19
+
20
+ /** Last non-empty line of a log tail, without colour codes. */
21
+ function lastLine(text) {
22
+ var lines = String(text || '').replace(/\x1b\[[0-9;?]*[A-Za-z]/g, '').split(/\r?\n/);
23
+ for (var i = lines.length - 1; i >= 0; i--) {
24
+ var line = lines[i].replace(/^\s+|\s+$/g, '');
25
+ if (line) return line.length > 240 ? line.slice(0, 240) + '…' : line;
26
+ }
27
+ return null;
28
+ }
29
+
30
+ function attemptsOf(task, running) {
31
+ var cb = task.circuitBreaker || {};
32
+ var attempt = (cb.consecutiveFailures || 0) + (running ? 1 : 0);
33
+ if (!attempt && task.status === 'blocked') {
34
+ var m = /(\d+)\s+(?:fix\s+)?attempts?/i.exec(task.notes || '');
35
+ attempt = m ? Number(m[1]) : 0;
36
+ }
37
+ return attempt || null;
38
+ }
39
+
40
+ function buildTask(t, milestoneId, index) {
41
+ var b = index.breakdown[t.id] || null;
42
+ var run = index.run[t.id] || null;
43
+ var esc1 = index.escalation[t.id] || null;
44
+ var running = t.status === 'in_progress';
45
+ var startT = b ? toTime(b.startedAt) : null;
46
+ if (startT == null && run && isRunActive(run)) startT = toTime(run.startedAt);
47
+ var cost = b && b.actual ? positive(b.actual.costUsd) : null;
48
+ if (cost == null && b && b.estimated) cost = positive(b.estimated.costUsd);
49
+ return {
50
+ id: t.id,
51
+ title: t.title || t.id,
52
+ description: t.description || '',
53
+ status: t.status,
54
+ agent: t.assignedSubagent || null,
55
+ deps: t.dependencies || [],
56
+ files: t.targetFiles || [],
57
+ verify: t.verificationCommand || '',
58
+ milestoneId: milestoneId,
59
+ children: [],
60
+ waitingOn: [],
61
+ isAvailable: !!t.isAvailable,
62
+ reason: t.status === 'blocked' && t.notes ? String(t.notes).replace(/^\s+|\s+$/g, '') || null : null,
63
+ attempt: attemptsOf(t, running),
64
+ maxAttempts: (t.circuitBreaker && t.circuitBreaker.maxThreshold) || 3,
65
+ startT: startT,
66
+ endT: t.status === 'completed' && b ? toTime(b.completedAt) : null,
67
+ durationMs: b ? positive(b.durationMs) : null,
68
+ checkMs: b && b.verification ? positive(b.verification.durationMs) : null,
69
+ costUsd: cost,
70
+ run: run,
71
+ latestLog: lastLine(index.logTail[t.id]),
72
+ escalation: esc1,
73
+ hasProposal: !!(esc1 && esc1.proposedPatch)
74
+ };
75
+ }
76
+
77
+ function indexApi(api) {
78
+ var index = { breakdown: {}, run: {}, escalation: {}, logTail: api.logTail || {} };
79
+ ((api.telemetry && api.telemetry.taskBreakdowns) || []).forEach(function (b) {
80
+ if (b && b.taskId) index.breakdown[b.taskId] = b;
81
+ });
82
+ (api.runs || []).forEach(function (r) {
83
+ var have = r && index.run[r.taskId];
84
+ if (r && r.taskId && (!have || (isRunActive(r) && !isRunActive(have)))) index.run[r.taskId] = r;
85
+ });
86
+ (api.escalations || []).forEach(function (e) {
87
+ var pending = e && (!e.status || e.status === 'pending_review');
88
+ if (pending && e.taskId && !index.escalation[e.taskId]) index.escalation[e.taskId] = e;
89
+ });
90
+ return index;
91
+ }
92
+
93
+ function finishMilestone(m) {
94
+ m.total = m.tasks.length;
95
+ m.done = m.tasks.filter(function (t) { return t.status === 'completed'; }).length;
96
+ var ends = m.tasks.map(function (t) { return t.endT || 0; });
97
+ m.lastEndT = Math.max.apply(null, [0].concat(ends)) || null;
98
+ m.workMs = m.tasks.reduce(function (sum, t) { return sum + (t.durationMs || 0); }, 0);
99
+ var costs = m.tasks.filter(function (t) { return t.costUsd != null; });
100
+ m.costUsd = costs.length ? costs.reduce(function (sum, t) { return sum + t.costUsd; }, 0) : null;
101
+ }
102
+
103
+ function linkTasks(tasks, byId) {
104
+ tasks.forEach(function (t) {
105
+ t.deps.forEach(function (d) {
106
+ var parent = byId[d];
107
+ if (parent && parent.children.indexOf(t.id) === -1) parent.children.push(t.id);
108
+ if (!parent || parent.status !== 'completed') t.waitingOn.push(d);
109
+ });
110
+ });
111
+ }
112
+
113
+ function needsYouItems(tasks, byId, escalations) {
114
+ var seen = {}, items = [];
115
+ var add = function (t) {
116
+ seen[t.id] = true;
117
+ items.push({ taskId: t.id, task: t, escalation: t.escalation, hasProposal: t.hasProposal,
118
+ kind: t.status === 'blocked' ? 'stuck' : 'question' });
119
+ };
120
+ tasks.forEach(function (t) { if (t.status === 'blocked') add(t); });
121
+ (escalations || []).forEach(function (e) {
122
+ var pending = e && (!e.status || e.status === 'pending_review');
123
+ if (pending && byId[e.taskId] && !seen[e.taskId]) add(byId[e.taskId]);
124
+ });
125
+ return items;
126
+ }
127
+
128
+ function summaryOf(api, milestones, tasks) {
129
+ var act = api.telemetry && api.telemetry.summary && api.telemetry.summary.actual;
130
+ var st = api.status && api.status.telemetry;
131
+ var pick = function (a, b) { return a != null && isFinite(a) ? a : (b != null && isFinite(b) ? b : null); };
132
+ var contracts = api.status && api.status.contracts;
133
+ var done = tasks.filter(function (t) { return t.status === 'completed'; }).length;
134
+ return {
135
+ spendUsd: pick(act && act.spendUsd, st && st.actualSpendUsd),
136
+ cacheHitRate: pick(act && act.cacheHitRate, st && st.cacheHitRate),
137
+ cacheSavingsUsd: pick(act && act.cacheSavingsUsd, null),
138
+ contractsPresent: contracts ? Object.keys(contracts).filter(function (k) { return contracts[k]; }).length : null,
139
+ contractsTotal: 5,
140
+ milestonesTotal: milestones.length,
141
+ milestonesDone: milestones.filter(function (m) { return m.total > 0 && m.done === m.total; }).length,
142
+ tasksTotal: tasks.length,
143
+ tasksDone: done,
144
+ tasksLeft: tasks.length - done
145
+ };
146
+ }
147
+
148
+ /** api = { milestones, status, telemetry, runs, escalations, logTail }, the raw slices from the server. */
149
+ function buildModel(api) {
150
+ api = api || {};
151
+ var index = indexApi(api);
152
+ var tasks = [], byId = {};
153
+ var milestones = (api.milestones || []).map(function (m, i) {
154
+ var list = (m.tasks || []).map(function (t) {
155
+ var task = buildTask(t, m.id, index);
156
+ byId[task.id] = task;
157
+ tasks.push(task);
158
+ return task;
159
+ });
160
+ return { id: m.id, name: m.name || m.id, short: shortName(m.name || m.id), status: m.status, idx: i, tasks: list };
161
+ });
162
+ linkTasks(tasks, byId);
163
+ milestones.forEach(finishMilestone);
164
+ var active = null;
165
+ milestones.some(function (m) {
166
+ var open = m.tasks.some(function (t) { return t.status !== 'completed'; });
167
+ if (open) active = m.id;
168
+ return open;
169
+ });
170
+ if (!active && milestones.length) active = milestones[milestones.length - 1].id;
171
+ return {
172
+ milestones: milestones,
173
+ tasks: tasks,
174
+ byId: byId,
175
+ activeMilestoneId: active,
176
+ running: tasks.filter(function (t) { return t.status === 'in_progress'; }),
177
+ stuck: tasks.filter(function (t) { return t.status === 'blocked'; }),
178
+ needsYou: needsYouItems(tasks, byId, api.escalations),
179
+ summary: summaryOf(api, milestones, tasks)
180
+ };
181
+ }
182
+
183
+ /** Stores the model and keeps the shared milestone selection valid. */
184
+ Studio.setModel = function (model) {
185
+ Studio.model = model;
186
+ var S = Studio.state;
187
+ var known = model.milestones.some(function (m) { return m.id === S.milestoneId; });
188
+ if (!known) S.milestoneId = model.activeMilestoneId;
189
+ };
190
+ Studio.buildModel = buildModel;
191
+
192
+ `;
@@ -0,0 +1 @@
1
+ export declare const paintScript: string;
@@ -0,0 +1,154 @@
1
+ // Painting: pages, sidebar, banner, plus small fragments the kept pages still use.
2
+ export const paintScript = String.raw ` // ─── Painting ──────────────────────────────────────────────────────────────
3
+ function cssEsc(v) { return window.CSS && CSS.escape ? CSS.escape(v) : String(v).replace(/["\\]/g, '\\$&'); }
4
+
5
+ function focusMemo(root) {
6
+ var el = document.activeElement;
7
+ if (!el || el === document.body || el === root || !root.contains(el)) return null;
8
+ var sel = null;
9
+ if (el.id) sel = '#' + cssEsc(el.id);
10
+ else if (el.getAttribute('data-act')) {
11
+ sel = '[data-act="' + cssEsc(el.getAttribute('data-act')) + '"]';
12
+ if (el.getAttribute('data-key')) sel += '[data-key="' + cssEsc(el.getAttribute('data-key')) + '"]';
13
+ } else if (el.hasAttribute('data-ms-select')) sel = '[data-ms-select]';
14
+ return sel ? { sel: sel, start: el.selectionStart, end: el.selectionEnd } : null;
15
+ }
16
+
17
+ function restoreFocus(root, memo) {
18
+ if (!memo) return;
19
+ var el = root.querySelector(memo.sel);
20
+ if (!el || el.disabled) return;
21
+ el.focus({ preventScroll: true });
22
+ if (memo.start != null && typeof el.setSelectionRange === 'function') {
23
+ try { el.setSelectionRange(memo.start, memo.end); } catch (e) { /* ignore */ }
24
+ }
25
+ }
26
+
27
+ /** A page is loading until every server slice it depends on has answered once. */
28
+ function isPageLoading(def) {
29
+ return (def.deps || []).some(function (d) { return PIPE_PATHS[d] && !pipe.loaded[d]; });
30
+ }
31
+
32
+ function pageCtx(panel) {
33
+ return { model: Studio.model, state: Studio.state, fmt: fmt, ui: ui, width: panel ? panel.clientWidth : 0 };
34
+ }
35
+
36
+ /** Renders one page into its panel; keeps focus and skips the write when nothing changed. */
37
+ function paintPage(id) {
38
+ var def = Studio.pages[id];
39
+ var panel = $('panel-' + id);
40
+ if (!def || !panel || !def.render) return;
41
+ var ctx = pageCtx(panel);
42
+ var html = isPageLoading(def) || !Studio.model ? (def.skeleton ? def.skeleton(ctx) : ui.skeleton()) : def.render(ctx);
43
+ panel.setAttribute('aria-busy', String(isPageLoading(def)));
44
+ if (panel.__html === html) return;
45
+ var memo = focusMemo(panel);
46
+ panel.innerHTML = html;
47
+ panel.__html = html;
48
+ restoreFocus(panel, memo);
49
+ }
50
+
51
+ /** Repaints a page (kept pages call this after their own state changes). */
52
+ function repaint(view) {
53
+ if (Studio.state.view !== view) return;
54
+ paintPage(view);
55
+ paintChrome();
56
+ }
57
+ Studio.repaint = function (id) { paintPage(id || Studio.state.view); };
58
+
59
+ function repaintFor(parts) {
60
+ var id = Studio.state.view;
61
+ var def = Studio.pages[id];
62
+ if (!def || !def.render) return;
63
+ if (!parts || (def.deps || []).some(function (d) { return parts.indexOf(d) !== -1; })) paintPage(id);
64
+ }
65
+
66
+ function paintChrome() {
67
+ var side = $('side-nav');
68
+ var main = mainWorktree();
69
+ var agents = pipe.worktrees.filter(function (w) { return w.isAgentWorktree; }).length;
70
+ var s = pipe.report && pipe.report.summary;
71
+ var navHtml = Studio.sidebarHtml({
72
+ model: Studio.model, view: Studio.state.view,
73
+ worktreeCount: pipe.loaded.worktrees && !pipe.errors.worktrees ? agents : null,
74
+ benchmarkText: s ? compactNum(Number(s.averageThroughputOpsPerSec) || 0) : ''
75
+ });
76
+ if (side.__html !== navHtml) { side.innerHTML = navHtml; side.__html = navHtml; }
77
+ var foot = $('side-foot');
78
+ var footHtml = Studio.sideFootHtml({ root: main ? String(main.path || '') : '', branch: main ? (main.branch || 'detached') : '' });
79
+ if (foot.__html !== footHtml) { foot.innerHTML = footHtml; foot.__html = footHtml; }
80
+ var lost = !!(pipe.errors.tasks || pipe.errors.status) || live.mode === 'offline';
81
+ $('banner').hidden = !lost;
82
+ }
83
+
84
+ /** One entry point after data or state changes: model, sidebar, current page, panel, palette. */
85
+ function refreshUi(parts) {
86
+ rebuildModel();
87
+ paintChrome();
88
+ repaintFor(parts);
89
+ refreshDetail();
90
+ refreshPalette();
91
+ backfillLogs();
92
+ }
93
+
94
+ var uiTimer = null, uiParts = {};
95
+ function queueUiRefresh(parts) {
96
+ parts.forEach(function (p) { uiParts[p] = true; });
97
+ if (uiTimer) return;
98
+ uiTimer = setTimeout(function () {
99
+ var list = Object.keys(uiParts);
100
+ uiParts = {};
101
+ uiTimer = null;
102
+ refreshUi(list);
103
+ }, 250);
104
+ }
105
+
106
+ /** Newest output line per task comes from the live stream and, once, from the log tail. */
107
+ function feedLogTail(entry) {
108
+ pipe.logTail[entry.taskId] = ((pipe.logTail[entry.taskId] || '') + (entry.chunk || '')).slice(-2000);
109
+ queueUiRefresh(['logs']);
110
+ }
111
+ var logAsked = {};
112
+ function backfillLogs() {
113
+ if (!Studio.model) return;
114
+ Studio.model.running.forEach(function (t) {
115
+ if (logAsked[t.id] || pipe.logTail[t.id] != null) return;
116
+ logAsked[t.id] = true;
117
+ api('/api/pipeline/tasks/logs?taskId=' + encodeURIComponent(t.id) + '&tailLines=20').then(function (body) {
118
+ pipe.logTail[t.id] = body.log || '';
119
+ queueUiRefresh(['logs']);
120
+ }, function () { /* no log yet */ });
121
+ });
122
+ }
123
+
124
+ // Fragments the kept pages (Worktrees, Benchmarks) still use.
125
+ function panelHead(title, lead, actions) {
126
+ return '<div class="panel-head"><div class="panel-title"><h1>' + title + '</h1>' + (lead ? '<p class="lead">' + lead + '</p>' : '') + '</div>' +
127
+ (actions ? '<div class="panel-actions">' + actions + '</div>' : '') + '</div>';
128
+ }
129
+
130
+ function progressBar(pct, key, label, tone) {
131
+ return '<div class="progress' + (tone ? ' ' + tone : '') + '" role="progressbar" aria-label="' + esc(label) + '" aria-valuemin="0" aria-valuemax="100" aria-valuenow="' + pct + '">' +
132
+ '<span style="width:' + pct + '%"></span></div>';
133
+ }
134
+
135
+ function errorCard(what, message, part) {
136
+ var missing = /No route for|NOT_FOUND|\(404\)/.test(message || '');
137
+ return '<div class="card state state-error" role="alert"><div class="state-icon">' + ICON.alert + '</div>' +
138
+ '<h2>Could not load ' + esc(what) + '</h2><p>' + esc(message) + '</p>' +
139
+ (missing ? '<p>This studio server does not expose the pipeline API yet. Restart it with a nativ build that includes the Mission Control endpoints.</p>' : '') +
140
+ '<button class="btn" type="button" data-act="retry" data-key="' + part + '">Retry</button></div>';
141
+ }
142
+
143
+ /** Data is still shown after a failed live refresh; say so instead of silently going stale. */
144
+ function staleNote(part) {
145
+ return pipe.errors[part] ? '<p class="notice" role="status">Showing the last synced data. Refresh failed: ' + esc(pipe.errors[part]) + '</p>' : '';
146
+ }
147
+
148
+ function skeletonCards(n, cls) {
149
+ var out = '';
150
+ for (var i = 0; i < n; i++) out += '<div class="card ' + (cls || 'kpi') + '"><div class="skeleton short"></div><div class="skeleton"></div><div class="skeleton"></div></div>';
151
+ return out;
152
+ }
153
+
154
+ `;
@@ -0,0 +1 @@
1
+ export declare const paletteViewScript: string;
@@ -0,0 +1,60 @@
1
+ // Pure command palette logic: what a query finds and how the list is drawn.
2
+ export const paletteViewScript = String.raw ` // ─── Command palette (pure) ─────────────────────────────────────────────────
3
+ var PALETTE_MAX = 9;
4
+
5
+ function taskHaystack(t) {
6
+ var info = agentInfo(t.agent);
7
+ return [t.id, t.title, t.agent || '', info.label].concat(t.files).join(' ').toLowerCase();
8
+ }
9
+
10
+ function emptyQueryTasks(model) {
11
+ var done = model.tasks.filter(function (t) { return t.status === 'completed'; });
12
+ var timed = done.filter(function (t) { return t.endT; }).sort(function (a, b) { return b.endT - a.endT; });
13
+ var rest = done.filter(function (t) { return !t.endT; }).reverse();
14
+ return model.running.concat(model.stuck, timed, rest);
15
+ }
16
+
17
+ function matchRank(t, q) {
18
+ var id = t.id.toLowerCase();
19
+ return id === q ? 0 : id.indexOf(q) === 0 ? 1 : 2;
20
+ }
21
+
22
+ /** Pages first ("Go to Flow"), then tasks matching id, title, agent id, agent label or any file. */
23
+ Studio.paletteItems = function (query) {
24
+ var model = Studio.model;
25
+ var q = String(query || '').replace(/^\s+|\s+$/g, '').toLowerCase();
26
+ var pages = q ? Studio.order.filter(function (p) { return p.label.toLowerCase().indexOf(q) !== -1; })
27
+ .map(function (p) { return { page: p }; }) : [];
28
+ var tasks = [];
29
+ if (model) {
30
+ tasks = q ? model.tasks.filter(function (t) { return taskHaystack(t).indexOf(q) !== -1; })
31
+ .sort(function (a, b) { return matchRank(a, q) - matchRank(b, q); }) : emptyQueryTasks(model);
32
+ }
33
+ return pages.concat(tasks.map(function (t) { return { task: t }; })).slice(0, PALETTE_MAX);
34
+ };
35
+
36
+ Studio.paletteFrame = function () {
37
+ return '<div class="pal" role="dialog" aria-label="Find a task" aria-modal="true">' +
38
+ '<input id="pal-q" type="text" placeholder="Search by task id, title, agent or file" autocomplete="off" ' +
39
+ 'role="combobox" aria-expanded="true" aria-controls="pal-list" aria-label="Find a task">' +
40
+ '<ul id="pal-list" role="listbox"></ul>' +
41
+ '<div class="hint"><span>Up and down to move</span><span>Enter to open</span><span>Esc to close</span></div></div>';
42
+ };
43
+
44
+ /** Rows for the listbox; sel is the highlighted index. */
45
+ Studio.paletteListHtml = function (items, sel, query) {
46
+ if (!items.length) {
47
+ return '<li class="faint pal-none">No task matches. Try an id like task-31 or a file name.</li>';
48
+ }
49
+ return items.map(function (it, i) {
50
+ var on = ' role="option" id="pal-opt-' + i + '" data-i="' + i + '" aria-selected="' + (i === sel) + '">';
51
+ if (it.page) {
52
+ return '<li' + on + '<span class="pal-ico">' + ui.svg(it.page.icon) + '</span><span class="t">Go to ' + esc(it.page.label) + '</span></li>';
53
+ }
54
+ var t = it.task;
55
+ return '<li' + on + ui.statusIcon(t.status) + '<span class="mono faint hide-sm">' + esc(t.id) + '</span>' +
56
+ '<span class="t">' + esc(t.title) + '</span><span class="hide-sm">' + ui.agentChip(t.agent) + '</span></li>';
57
+ }).join('');
58
+ };
59
+
60
+ `;
@@ -0,0 +1 @@
1
+ export declare const paletteScript: string;
@@ -0,0 +1,80 @@
1
+ // Command palette: Ctrl+K / Cmd+K / "/" to open, arrows and Enter to pick, Esc to close.
2
+ export const paletteScript = String.raw ` // ─── Command palette ───────────────────────────────────────────────────────
3
+ var pal = { open: false, sel: 0, items: [], opener: null };
4
+
5
+ function drawPalette() {
6
+ var q = $('pal-q');
7
+ pal.items = Studio.paletteItems(q ? q.value : '');
8
+ if (pal.sel >= pal.items.length) pal.sel = 0;
9
+ $('pal-list').innerHTML = Studio.paletteListHtml(pal.items, pal.sel);
10
+ if (q) q.setAttribute('aria-activedescendant', pal.items.length ? 'pal-opt-' + pal.sel : '');
11
+ var cur = $('pal-opt-' + pal.sel);
12
+ if (cur && cur.scrollIntoView) cur.scrollIntoView({ block: 'nearest' });
13
+ }
14
+
15
+ function openPalette() {
16
+ if (pal.open) return;
17
+ pal.open = true;
18
+ pal.sel = 0;
19
+ pal.opener = document.activeElement;
20
+ $('pal').innerHTML = Studio.paletteFrame();
21
+ $('pal').hidden = false;
22
+ drawPalette();
23
+ $('pal-q').focus();
24
+ }
25
+
26
+ function closePalette() {
27
+ if (!pal.open) return;
28
+ pal.open = false;
29
+ $('pal').hidden = true;
30
+ $('pal').innerHTML = '';
31
+ if (pal.opener && document.body.contains(pal.opener) && pal.opener.focus) pal.opener.focus();
32
+ }
33
+
34
+ function refreshPalette() { if (pal.open) drawPalette(); }
35
+
36
+ function pickPalette(i) {
37
+ var it = pal.items[i];
38
+ if (!it) return;
39
+ pal.opener = null;
40
+ closePalette();
41
+ if (it.page) { Studio.go(it.page.id); return; }
42
+ Studio.state.milestoneId = it.task.milestoneId;
43
+ openDetail(it.task.id);
44
+ repaintFor(null);
45
+ }
46
+
47
+ $('btn-find').addEventListener('click', openPalette);
48
+ $('pal').addEventListener('click', function (e) {
49
+ var li = e.target.closest('li[data-i]');
50
+ if (li) { pickPalette(Number(li.getAttribute('data-i'))); return; }
51
+ if (e.target.id === 'pal') closePalette();
52
+ });
53
+ $('pal').addEventListener('input', function () { pal.sel = 0; drawPalette(); });
54
+
55
+ function typingInField() {
56
+ var a = document.activeElement;
57
+ return !!a && /^(input|select|textarea)$/i.test(a.tagName);
58
+ }
59
+
60
+ document.addEventListener('keydown', function (e) {
61
+ if ((e.ctrlKey || e.metaKey) && String(e.key).toLowerCase() === 'k') {
62
+ e.preventDefault();
63
+ if (pal.open) closePalette(); else openPalette();
64
+ return;
65
+ }
66
+ if (e.key === '/' && !pal.open && !typingInField() && !e.ctrlKey && !e.metaKey) { e.preventDefault(); openPalette(); return; }
67
+ if (pal.open) {
68
+ var n = Math.max(1, pal.items.length);
69
+ if (e.key === 'Escape') { e.preventDefault(); closePalette(); }
70
+ else if (e.key === 'ArrowDown' || e.key === 'ArrowUp') {
71
+ e.preventDefault();
72
+ pal.sel = (pal.sel + (e.key === 'ArrowDown' ? 1 : -1) + n) % n;
73
+ drawPalette();
74
+ } else if (e.key === 'Enter') { e.preventDefault(); pickPalette(pal.sel); }
75
+ return;
76
+ }
77
+ if (e.key === 'Escape' && Studio.state.detailTaskId && !document.querySelector('dialog[open]')) closeDetail(false);
78
+ });
79
+
80
+ `;
@@ -0,0 +1 @@
1
+ export declare const pipelineStateScript: string;
@@ -0,0 +1,152 @@
1
+ // Pipeline data store, fetching, and the model rebuild that follows every change.
2
+ export const pipelineStateScript = String.raw ` // ─── Pipeline state ────────────────────────────────────────────────────────
3
+ var PIPE_PATHS = {
4
+ status: '/api/pipeline/status',
5
+ tasks: '/api/pipeline/tasks',
6
+ worktrees: '/api/pipeline/worktrees',
7
+ benchmarks: '/api/pipeline/benchmarks',
8
+ runs: '/api/pipeline/tasks/runs',
9
+ telemetry: '/api/pipeline/telemetry/detailed',
10
+ escalations: '/api/pipeline/escalations?status=pending_review',
11
+ triage: '/api/pipeline/triage/status'
12
+ };
13
+ var ALL_PARTS = ['status', 'tasks', 'worktrees', 'benchmarks', 'runs', 'telemetry', 'escalations', 'triage'];
14
+ var REDUCED_MOTION = window.matchMedia ? window.matchMedia('(prefers-reduced-motion: reduce)') : { matches: false };
15
+
16
+ var pipe = {
17
+ status: null,
18
+ milestones: [],
19
+ worktrees: [],
20
+ runs: [],
21
+ report: null,
22
+ telemetry: null,
23
+ escalations: [],
24
+ triage: null,
25
+ triageRunning: {},
26
+ triageRecent: {},
27
+ triageLast: null,
28
+ logTail: {},
29
+ loaded: {},
30
+ errors: {},
31
+ busy: {},
32
+ syncedAt: null
33
+ };
34
+
35
+ function allTasks() {
36
+ var out = [];
37
+ (pipe.milestones || []).forEach(function (m) { (m.tasks || []).forEach(function (t) { out.push(t); }); });
38
+ return out;
39
+ }
40
+ function taskById(id) {
41
+ var found = null;
42
+ allTasks().some(function (t) { if (t.id === id) { found = t; return true; } return false; });
43
+ return found;
44
+ }
45
+
46
+ function setProjectName(name) {
47
+ if (!name) return;
48
+ var el = $('project-name');
49
+ if (el) { el.textContent = name; el.hidden = false; }
50
+ document.title = 'Nativ Studio · ' + name;
51
+ }
52
+
53
+ function fetchPipeline(parts) {
54
+ return Promise.all(parts.map(function (part) {
55
+ return api(PIPE_PATHS[part]).then(function (body) {
56
+ applyPipeline(part, body);
57
+ pipe.errors[part] = null;
58
+ }, function (e) {
59
+ pipe.errors[part] = e.message;
60
+ }).then(function () { pipe.loaded[part] = true; });
61
+ })).then(function () {
62
+ pipe.syncedAt = new Date();
63
+ refreshUi(parts);
64
+ });
65
+ }
66
+
67
+ function applyPipeline(part, body) {
68
+ if (part === 'status') {
69
+ pipe.status = body.pipeline || null;
70
+ if (pipe.status) setProjectName(pipe.status.projectName);
71
+ } else if (part === 'tasks') {
72
+ pipe.milestones = Array.isArray(body.milestones) ? body.milestones : [];
73
+ setProjectName(body.projectName);
74
+ } else if (part === 'worktrees') {
75
+ pipe.worktrees = Array.isArray(body.worktrees) ? body.worktrees : [];
76
+ } else if (part === 'runs') {
77
+ pipe.runs = Array.isArray(body.runs) ? body.runs : [];
78
+ } else if (part === 'benchmarks') {
79
+ pipe.report = body.report || null;
80
+ } else if (part === 'telemetry') {
81
+ pipe.telemetry = { summary: body.summary || {}, taskBreakdowns: Array.isArray(body.taskBreakdowns) ? body.taskBreakdowns : [] };
82
+ } else if (part === 'escalations') {
83
+ pipe.escalations = Array.isArray(body.escalations) ? body.escalations : [];
84
+ syncHealTab();
85
+ renderTriageChip();
86
+ } else if (part === 'triage') {
87
+ pipe.triage = body;
88
+ renderTriageChip();
89
+ }
90
+ }
91
+
92
+ /** Rebuilds the client model from the raw slices. */
93
+ function rebuildModel() {
94
+ Studio.setModel(buildModel({
95
+ milestones: pipe.milestones,
96
+ status: pipe.status,
97
+ telemetry: pipe.telemetry,
98
+ runs: pipe.runs,
99
+ escalations: pipe.escalations,
100
+ logTail: pipe.logTail
101
+ }));
102
+ }
103
+
104
+ /** Newest pending escalation for a task (the list arrives newest first). */
105
+ function escalationFor(taskId) {
106
+ var found = null;
107
+ (pipe.escalations || []).some(function (e) { if (e.taskId === taskId && e.status === 'pending_review') { found = e; return true; } return false; });
108
+ return found;
109
+ }
110
+
111
+ /** The main (non-agent) worktree is the workspace root and carries the active branch. */
112
+ function mainWorktree() {
113
+ var main = null;
114
+ pipe.worktrees.some(function (w) { if (!w.isAgentWorktree) { main = w; return true; } return false; });
115
+ return main;
116
+ }
117
+
118
+ /** 1234 becomes "1.2k" so the sidebar badge stays small. */
119
+ function compactNum(n) {
120
+ if (!isFinite(n)) return '–';
121
+ if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
122
+ if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, '') + 'k';
123
+ return String(Math.round(n));
124
+ }
125
+
126
+ /** Newest Just-finished style helpers live in pages; here only what the runner tools need. */
127
+ var RUN_LABEL = {
128
+ spawning_worktree: 'Worktree', running: 'Claude Running', verifying: 'Verifying', merging: 'Merging',
129
+ completed: 'Run completed', failed: 'Run failed', aborted: 'Run aborted'
130
+ };
131
+ function runFor(taskId) {
132
+ var found = null;
133
+ (pipe.runs || []).some(function (r) { if (r.taskId === taskId) { found = r; return true; } return false; });
134
+ return found;
135
+ }
136
+ function upsertRun(run) {
137
+ if (!run || !run.taskId) return;
138
+ pipe.runs = [run].concat((pipe.runs || []).filter(function (r) { return r.taskId !== run.taskId; }));
139
+ }
140
+ function elapsedOf(run) {
141
+ if (!run || !run.startedAt) return null;
142
+ var ms = isRunActive(run) ? Date.now() - Date.parse(run.startedAt) : run.durationMs;
143
+ return typeof ms === 'number' && isFinite(ms) && ms >= 0 ? ms : null;
144
+ }
145
+ /** mm:ss elapsed time shown in the console drawer. */
146
+ function mmss(ms) {
147
+ var total = Math.floor((ms || 0) / 1000);
148
+ var mm = Math.floor(total / 60), ss = total % 60;
149
+ return (mm < 10 ? '0' : '') + mm + ':' + (ss < 10 ? '0' : '') + ss;
150
+ }
151
+
152
+ `;
@@ -0,0 +1 @@
1
+ export declare const proposalsScript: string;