create-pmos 0.101.0 → 0.103.0
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 +1 -1
- package/payload/KIT-VERSION +1 -1
- package/payload/planning/evals/eval-calibration.md +1 -1
- package/payload/planning/okrs/health-budgets.md +1 -1
- package/payload/state/discoveries.md +1 -1
- package/workspace/lib/kit-update.js +15 -1
- package/workspace/package-lock.json +2 -2
- package/workspace/package.json +1 -1
- package/workspace/public/pm/app.js +2 -1
- package/workspace/public/pm/components.css +2 -2
- package/workspace/public/pm/dest-settings.js +10 -5
- package/workspace/public/pm/panel-cockpit.js +1 -0
- package/workspace/public/pm/topbar.js +1 -1
- package/workspace/public/pm/view-notes-menu.js +16 -14
- package/workspace/run-tests.js +27 -2
- package/workspace/server.js +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-pmos",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.103.0",
|
|
4
4
|
"description": "Scaffold the PMOS portable kit — run the PMOS product-management workflow (initiative → lane-fit spec → contract → build → three gates, with work-type lanes for features, research, maintenance, incidents, and content) entirely inside your own repo, with all state as git-versioned files. Includes `workflow-sweep`, which audits the state every board derives from and separates real defects from work you simply have not done. `create-pmos workspace` also installs + launches the local Terminal Workspace GUI. No database, no network, no telemetry.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
package/payload/KIT-VERSION
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Eval calibration log"
|
|
4
4
|
description: "Append-only record of every PM override of an evaluator verdict."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-08T00:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-08T00:46:10Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Health budgets"
|
|
4
4
|
description: "Closed registry of health-budget anchors (D59), edited only by the PM."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-08T00:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-08T00:46:10Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
type: Reference
|
|
3
3
|
title: "Discoveries and friction log"
|
|
4
4
|
description: "Append-only record of what runs discovered and where the process rubbed."
|
|
5
|
-
generated: {by: process:create-pmos, at: "2026-10-08T00:
|
|
5
|
+
generated: {by: process:create-pmos, at: "2026-10-08T00:46:10Z"}
|
|
6
6
|
status: stable
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -166,6 +166,20 @@ async function latest({ exec = cp.execFile, source: src = source() } = {}) {
|
|
|
166
166
|
return { ok: true, version: v };
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
/** Check the local repository's package version (for dev/testing without npm). Reads npm-pkg/package.json for kit. */
|
|
170
|
+
function latestLocal({ repoRoot = null } = {}) {
|
|
171
|
+
try {
|
|
172
|
+
const pkgPath = path.join(String(repoRoot || ''), 'npm-pkg', 'package.json');
|
|
173
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
174
|
+
if (!pkg.version || !validVersion(pkg.version)) {
|
|
175
|
+
return { ok: false, error: 'Local package.json has no valid version' };
|
|
176
|
+
}
|
|
177
|
+
return { ok: true, version: pkg.version, source: 'local-git' };
|
|
178
|
+
} catch (e) {
|
|
179
|
+
return { ok: false, error: `Could not read local version: ${e.message}` };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
169
183
|
/**
|
|
170
184
|
* npm lists a new version in the check a moment before its download is served everywhere. A download asked for in that
|
|
171
185
|
* gap answers 404, and npm's CDN keeps the 404 for up to five minutes (Cache-Control max-age=300), so the same press
|
|
@@ -250,4 +264,4 @@ function upgradeApp(root, { version, logFile, spawn = cp.spawn, env = updateEnv(
|
|
|
250
264
|
}
|
|
251
265
|
}
|
|
252
266
|
|
|
253
|
-
module.exports = { PKG_NAME, cmpVer, notServedYet, validVersion, source, npxArgs, readKit, fromCheckout, status, compare, latest, updateKit, updateEnv, upgradeApp, whereFrom };
|
|
267
|
+
module.exports = { PKG_NAME, cmpVer, notServedYet, validVersion, source, npxArgs, readKit, fromCheckout, status, compare, latest, latestLocal, updateKit, updateEnv, upgradeApp, whereFrom };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pmos-workspace",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.103.0",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "pmos-workspace",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.103.0",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@xterm/addon-fit": "^0.11.0",
|
package/workspace/package.json
CHANGED
|
@@ -38,7 +38,6 @@ import { state, setState, subscribe, flush, FIXTURE, LEGACY_LAYOUT_FLAG } from '
|
|
|
38
38
|
import { isMobile, mountMobile, applyMobile } from './mobile.js';
|
|
39
39
|
/* The frame's parts (Run 3, slice 3A). Small modules with no effect at import: a fixture page loads them and calls none. */
|
|
40
40
|
import { createTopbar, unsyncedOf } from './topbar.js';
|
|
41
|
-
import { openView } from './panel-cockpit.js';
|
|
42
41
|
import { createDockRail } from './dock.js';
|
|
43
42
|
import { SEL, ensureComponentsCss, iconButton, menu, toast, sheet, button } from './primitives.js';
|
|
44
43
|
import { ensureSprite } from './icons.js';
|
|
@@ -608,6 +607,8 @@ const frEnd = el('span', { class: 'fr-end', 'aria-hidden': 'true' });
|
|
|
608
607
|
function setupFrame() {
|
|
609
608
|
ensureComponentsCss();
|
|
610
609
|
ensureSprite();
|
|
610
|
+
// panel-cockpit.js loads on first use: importing it statically here broke the legacy detail column's fold control (ui-test AC1)
|
|
611
|
+
const openView = (v) => import('./panel-cockpit.js').then((m) => m.openView(v));
|
|
611
612
|
bar = createTopbar({ go, openPalette, openView, repoMenu, sync: runSync });
|
|
612
613
|
document.addEventListener('pointerdown', (e) => { fr.downAt = e.target; }, true);
|
|
613
614
|
dockRail = createDockRail({ open: openDock, resize: dockWidth });
|
|
@@ -7421,7 +7421,7 @@
|
|
|
7421
7421
|
margin-bottom: 16px;
|
|
7422
7422
|
padding: 12px;
|
|
7423
7423
|
background: var(--secondary);
|
|
7424
|
-
border-radius:
|
|
7424
|
+
border-radius: 8px;
|
|
7425
7425
|
font-size: 13px;
|
|
7426
7426
|
}
|
|
7427
7427
|
.nt-detail-summary pre {
|
|
@@ -7444,7 +7444,7 @@
|
|
|
7444
7444
|
padding: 12px;
|
|
7445
7445
|
margin-bottom: 8px;
|
|
7446
7446
|
background: var(--secondary);
|
|
7447
|
-
border-radius:
|
|
7447
|
+
border-radius: 8px;
|
|
7448
7448
|
border-left: 3px solid var(--primary);
|
|
7449
7449
|
}
|
|
7450
7450
|
.nt-detail-action-text {
|
|
@@ -395,12 +395,12 @@ function readKitStatus(ws, force) {
|
|
|
395
395
|
|
|
396
396
|
const say = (fields) => { Object.assign(upd, fields); setState({}); };
|
|
397
397
|
|
|
398
|
-
async function checkUpdates(ws) {
|
|
398
|
+
async function checkUpdates(ws, local) {
|
|
399
399
|
say({ phase: 'check', message: '', error: '', output: '' });
|
|
400
|
-
const j = await pmPost('/api/kit/check', { ws });
|
|
400
|
+
const j = await pmPost('/api/kit/check', { ws, local });
|
|
401
401
|
if (j && j.ok !== false) {
|
|
402
402
|
kitByWs.set(ws, { st: j, at: Date.now() });
|
|
403
|
-
say({ phase: null, latest: j.latest, compare: { appBehind: !!j.appBehind, kitBehind: !!j.kitBehind } });
|
|
403
|
+
say({ phase: null, latest: j.latest, compare: { appBehind: !!j.appBehind, kitBehind: !!j.kitBehind }, source: j.source });
|
|
404
404
|
} else say({ phase: null, error: (j && j.error) || P.updateFailed });
|
|
405
405
|
}
|
|
406
406
|
|
|
@@ -480,9 +480,14 @@ function updatesSection(scope) {
|
|
|
480
480
|
onClick: () => updateAll(ws, app, { appBehind: !!cmp.appBehind, kitBehind: !!cmp.kitBehind && kitPresent }),
|
|
481
481
|
})));
|
|
482
482
|
}
|
|
483
|
+
kids.push(el('div', { class: 'fr-set__line' }, el('label', { class: 'fr-set__checkbox' },
|
|
484
|
+
el('input', { type: 'checkbox', checked: !!upd.checkLocal, onChange: (e) => { upd.checkLocal = e.target.checked; setState({}); } }),
|
|
485
|
+
el('span', null, 'Check local git version')
|
|
486
|
+
)));
|
|
483
487
|
const line = upd.phase === 'check' ? P.updatesChecking : upd.phase === 'kit' ? P.updateKitBusy
|
|
484
488
|
: upd.message ? upd.message : upd.latest ? (cmp.appBehind || cmp.kitBehind ? P.updatesLatest(upd.latest) : P.updatesCurrent(upd.latest)) : P.updatesNever;
|
|
485
|
-
|
|
489
|
+
const sourceNote = upd.source === 'local-git' ? ' (from local git)' : upd.source ? ` (from ${upd.source})` : '';
|
|
490
|
+
kids.push(el('p', { class: 'fr-set__note', role: 'status', 'aria-live': 'polite', 'data-fr': 'update-state' }, line + sourceNote));
|
|
486
491
|
if (upd.error) kids.push(el('p', { class: 'fr-set__note fr-set__error', role: 'alert', 'data-fr': 'update-error' }, upd.error));
|
|
487
492
|
if (upd.output) kids.push(el('pre', { class: 'fr-set__output', 'data-fr': 'update-output' }, upd.output));
|
|
488
493
|
kids.push(el('p', { class: 'fr-set__note' }, P.updatesNote));
|
|
@@ -495,7 +500,7 @@ function checkButton(scope) {
|
|
|
495
500
|
const ws = wsOfRepo(repo) || wsOfRepo(activeProduct());
|
|
496
501
|
return button({
|
|
497
502
|
label: P.updatesCheck, variant: 'outline', size: 'sm', icon: 'refresh-cw', disabled: !!upd.phase || !ws,
|
|
498
|
-
attrs: { 'data-fr': 'check-updates' }, onClick: () => checkUpdates(ws),
|
|
503
|
+
attrs: { 'data-fr': 'check-updates' }, onClick: () => checkUpdates(ws, upd.checkLocal),
|
|
499
504
|
});
|
|
500
505
|
}
|
|
501
506
|
|
|
@@ -124,6 +124,7 @@ export function mountView(host, tab) {
|
|
|
124
124
|
if (!v) inner.appendChild(el('div', { style: 'color:var(--muted-foreground);font-size:12px' }, 'this view is not known to this version'));
|
|
125
125
|
else if (board) {
|
|
126
126
|
inner.style.maxWidth = 'none';
|
|
127
|
+
// a board draws into `inner` and returns nothing (view-notes.js, view-today.js …); calling .then on that threw
|
|
127
128
|
try { board.renderBoard(inner, { tab, view: tab.view, renderMode, ready: !v.mode || !!m, el, setChildren, state, setState, FIXTURE, openView, repo: currentProduct() }); }
|
|
128
129
|
catch (e) { console.error('[pm] board ' + tab.view + ' failed:', e); renderMode(inner); }
|
|
129
130
|
} else if (boardMods[tab.view] === null) {
|
|
@@ -89,7 +89,7 @@ export function createTopbar({ go, openPalette, openView, repoMenu, sync } = {})
|
|
|
89
89
|
|
|
90
90
|
const notes = button({
|
|
91
91
|
label: 'Notes', variant: 'outline', icon: 'notebook-pen',
|
|
92
|
-
attrs: { class: 'fr-notes', 'data-fr-view': 'notes', title: 'Open notes menu (all notes by initiative)' },
|
|
92
|
+
attrs: { class: 'fr-notes', 'data-fr': 'notes', 'data-fr-view': 'notes', title: 'Open notes menu (all notes by initiative)' },
|
|
93
93
|
onClick: () => openView && openView('notesmenu'),
|
|
94
94
|
});
|
|
95
95
|
|
|
@@ -42,7 +42,8 @@ async function loadNote(id) {
|
|
|
42
42
|
function groupByInitiative(notes) {
|
|
43
43
|
const groups = {};
|
|
44
44
|
notes.forEach((n) => {
|
|
45
|
-
|
|
45
|
+
// a note's initiative is a link object ({ id, title, by }), never a bare string
|
|
46
|
+
const key = n.initiative ? (n.initiative.title || n.initiative.id) : '(no initiative)';
|
|
46
47
|
if (!groups[key]) groups[key] = [];
|
|
47
48
|
groups[key].push(n);
|
|
48
49
|
});
|
|
@@ -54,14 +55,8 @@ function groupByInitiative(notes) {
|
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
function renderNoteRow(note, isSelected, onClick) {
|
|
57
|
-
|
|
58
|
-
const summaryText =
|
|
59
|
-
.split('\n')
|
|
60
|
-
.slice(0, 2)
|
|
61
|
-
.map((line) => line.trim())
|
|
62
|
-
.filter(Boolean)
|
|
63
|
-
.join(' ')
|
|
64
|
-
.substring(0, 60);
|
|
58
|
+
// the list row carries summary_lines (up to three plain lines); its `summary` is only a has-a-summary flag
|
|
59
|
+
const summaryText = (note.summary_lines || []).slice(0, 2).join(' ').substring(0, 60);
|
|
65
60
|
|
|
66
61
|
return el('div', {
|
|
67
62
|
class: 'nt-menu-row' + (isSelected ? ' nt-menu-row--selected' : ''),
|
|
@@ -74,6 +69,13 @@ function renderNoteRow(note, isSelected, onClick) {
|
|
|
74
69
|
);
|
|
75
70
|
}
|
|
76
71
|
|
|
72
|
+
/** The note's summary as plain text: the agent's markdown, else the local overview lines. */
|
|
73
|
+
function summaryOf(note) {
|
|
74
|
+
if (note.agent && note.agent.markdown) return String(note.agent.markdown);
|
|
75
|
+
const ov = note.summary && note.summary.sections && note.summary.sections.overview;
|
|
76
|
+
return Array.isArray(ov) ? ov.map((x) => String((x && x.text) || '')).filter(Boolean).join('\n') : '';
|
|
77
|
+
}
|
|
78
|
+
|
|
77
79
|
function renderLeftPanel(notes, selectedId, onSelectNote, onToggleInitiative) {
|
|
78
80
|
const groups = groupByInitiative(notes);
|
|
79
81
|
|
|
@@ -98,9 +100,11 @@ function renderLeftPanel(notes, selectedId, onSelectNote, onToggleInitiative) {
|
|
|
98
100
|
);
|
|
99
101
|
}
|
|
100
102
|
|
|
101
|
-
|
|
103
|
+
// the cockpit calls renderBoard(inner, ctx) and ignores what it returns: the view draws into `inner`
|
|
104
|
+
export function renderBoard(inner, ctx) {
|
|
102
105
|
ensureComponentsCss();
|
|
103
106
|
const root = el('div', { class: 'nt-menu' });
|
|
107
|
+
inner.appendChild(root);
|
|
104
108
|
|
|
105
109
|
async function render() {
|
|
106
110
|
if (!list) await loadList();
|
|
@@ -142,7 +146,7 @@ export async function renderBoard(inner, ctx) {
|
|
|
142
146
|
el('div', { class: 'nt-detail-date' }, new Date(note.updated_at).toLocaleDateString()),
|
|
143
147
|
),
|
|
144
148
|
el('div', { class: 'nt-detail-summary' },
|
|
145
|
-
note
|
|
149
|
+
summaryOf(note) ? el('pre', {}, summaryOf(note)) : el('p', { class: 'nt-hint' }, 'No summary yet'),
|
|
146
150
|
),
|
|
147
151
|
actions.length > 0 && el('div', { class: 'nt-detail-actions' },
|
|
148
152
|
el('div', { class: 'nt-detail-actions-title' }, 'Next steps'),
|
|
@@ -184,7 +188,5 @@ export async function renderBoard(inner, ctx) {
|
|
|
184
188
|
setChildren(root, [leftPanel, rightPanel]);
|
|
185
189
|
}
|
|
186
190
|
|
|
187
|
-
|
|
188
|
-
ctx.setState = (state) => { render(); };
|
|
189
|
-
return root;
|
|
191
|
+
render();
|
|
190
192
|
}
|
package/workspace/run-tests.js
CHANGED
|
@@ -23,6 +23,11 @@
|
|
|
23
23
|
* CI for the browser group on a PR (ci-browser-after-merge): every browser suite still runs after the
|
|
24
24
|
* merge on main, and kit-publish waits for that run. See changedSuites() for the rule.
|
|
25
25
|
*
|
|
26
|
+
* --skip-passed=FILE|DIR --since=SHA skips a suite FILE (or any file in DIR) lists as passed (an earlier run of the same PR wrote it with
|
|
27
|
+
* --passed-out) when the diff SHA..HEAD cannot reach it, by the same rule as --changed. So the next push of a PR runs
|
|
28
|
+
* the suites that failed and the suites its new commits reach, not the whole group again. A suite it skips is written
|
|
29
|
+
* to --passed-out too, so a pass carries forward to the run after. An empty or unknown SHA skips nothing.
|
|
30
|
+
*
|
|
26
31
|
* Output: a suite's own output is buffered and printed whole when it finishes, so lines from two
|
|
27
32
|
* suites never interleave. A passing suite prints its last lines; --verbose prints all of it.
|
|
28
33
|
*
|
|
@@ -143,14 +148,15 @@ const WIDE = new Set([
|
|
|
143
148
|
* name carries the changed file's stem with any panel-/view-/tab- prefix removed (view-memory.js ->
|
|
144
149
|
* pm-r29-memory-test.js). It errs toward running MORE: a WIDE file, or a changed workspace file that
|
|
145
150
|
* no suite can be traced to, selects the whole list. Returns { files, suites, why }. */
|
|
146
|
-
function changedSuites(list, ref) {
|
|
151
|
+
function changedSuites(list, ref, { exact = false } = {}) {
|
|
147
152
|
const git = (...a) => {
|
|
148
153
|
const r = require('child_process').spawnSync('git', a, { cwd: DIR, encoding: 'utf8' });
|
|
149
154
|
return r.status === 0 ? r.stdout : null;
|
|
150
155
|
};
|
|
151
156
|
const top = (git('rev-parse', '--show-toplevel') || '').trim();
|
|
152
157
|
if (!top) return { files: [], suites: list, why: 'not a git checkout, so every suite runs' };
|
|
153
|
-
|
|
158
|
+
// exact: diff from `ref` itself (an earlier head of this branch), not from its merge base
|
|
159
|
+
const base = exact ? ((git('rev-parse', '--verify', '--quiet', `${ref}^{commit}`) || '').trim()) : (git('merge-base', 'HEAD', ref) || '').trim();
|
|
154
160
|
if (!base) return { files: [], suites: list, why: `no merge base with ${ref} (fetch it first), so every suite runs` };
|
|
155
161
|
const prefix = path.relative(top, DIR).split(path.sep).join('/') + '/';
|
|
156
162
|
const names = [
|
|
@@ -260,6 +266,24 @@ async function main() {
|
|
|
260
266
|
list = pick.suites;
|
|
261
267
|
if (!list.length) return;
|
|
262
268
|
}
|
|
269
|
+
// --skip-passed / --since: suites that passed on an earlier head of this PR and that the new commits cannot reach
|
|
270
|
+
let carried = [];
|
|
271
|
+
const passedIn = flag('skip-passed'); const since = flag('since');
|
|
272
|
+
if (passedIn && passedIn !== true && since && since !== true) {
|
|
273
|
+
let before = [];
|
|
274
|
+
// a file, or a directory of the files every job of the earlier run wrote (CI downloads them into one)
|
|
275
|
+
const read = (f) => { try { return fs.readFileSync(f, 'utf8').split('\n').map((x) => x.trim()).filter(Boolean); } catch { return []; } };
|
|
276
|
+
try { before = fs.statSync(passedIn).isDirectory() ? fs.readdirSync(passedIn).flatMap((n) => read(path.join(passedIn, n))) : read(passedIn); } catch { /* no earlier run: skip nothing */ }
|
|
277
|
+
const reach = changedSuites(list, since, { exact: true });
|
|
278
|
+
const reached = new Set(reach.suites);
|
|
279
|
+
const known = !/no merge base|not a git checkout/.test(reach.why);
|
|
280
|
+
carried = known ? list.filter((f) => before.includes(f) && !reached.has(f)) : [];
|
|
281
|
+
console.log(`run-tests: --skip-passed since ${since.slice(0, 12)}: ${known ? reach.why : 'that commit is not in this checkout, so nothing is skipped'}. ${carried.length} suite(s) passed before and are not reached; ${list.length - carried.length} run.`);
|
|
282
|
+
list = list.filter((f) => !carried.includes(f));
|
|
283
|
+
}
|
|
284
|
+
const passedOut = flag('passed-out');
|
|
285
|
+
const writePassed = (ran) => { if (passedOut && passedOut !== true) fs.writeFileSync(passedOut, [...carried, ...ran.filter((r) => r.ok).map((r) => r.file)].join('\n') + '\n'); };
|
|
286
|
+
if (!list.length) { writePassed([]); console.log('run-tests: nothing left to run.'); return; }
|
|
263
287
|
if (flag('list')) { console.log(list.join('\n')); return; }
|
|
264
288
|
const cpus = typeof os.availableParallelism === 'function' ? os.availableParallelism() : os.cpus().length;
|
|
265
289
|
// A browser suite runs a server, a tmux daemon and a Chromium of its own, and several of them assert
|
|
@@ -286,6 +310,7 @@ async function main() {
|
|
|
286
310
|
const failed = results.filter((r) => !r.ok);
|
|
287
311
|
const slowest = [...results].sort((a, b) => b.secs - a.secs).slice(0, 5).map((r) => `${r.file} ${r.secs}s`).join(', ');
|
|
288
312
|
const timingsOut = flag('timings-out');
|
|
313
|
+
writePassed(results);
|
|
289
314
|
if (timingsOut && timingsOut !== true) fs.writeFileSync(timingsOut, JSON.stringify(Object.fromEntries(results.map((r) => [r.file, r.secs])), null, 2));
|
|
290
315
|
console.log(`\nrun-tests: ${results.length - failed.length}/${results.length} suites passed in ${((Date.now() - t0) / 1000).toFixed(1)}s wall. Slowest: ${slowest}.`);
|
|
291
316
|
// exitCode, never process.exit(): stdout to a pipe is asynchronous, and exiting here cut the CI log
|
package/workspace/server.js
CHANGED
|
@@ -7329,12 +7329,12 @@ async function handleRequest(req, res) {
|
|
|
7329
7329
|
// the page did not show.
|
|
7330
7330
|
const w = body.ws ? workspaces.get(String(body.ws)) : null;
|
|
7331
7331
|
if (!w) return json(res, { ok: false, error: 'no such workspace' }, 400);
|
|
7332
|
-
const st = () => kitUpdate.status({ root: w.root, appVersion: APP_VERSION, appDir: __dirname });
|
|
7332
|
+
const st = () => kitUpdate.status({ root: w.root, appVersion: APP_VERSION, appDir: __dirname, env: process.env });
|
|
7333
7333
|
if (p === '/api/kit/check') {
|
|
7334
7334
|
const r = await kitUpdate.latest({ source: kitUpdate.source() });
|
|
7335
7335
|
const now = st();
|
|
7336
7336
|
if (!r.ok) return json(res, { ok: false, error: r.error, ws: w.id, ...now }, 502);
|
|
7337
|
-
return json(res, { ok: true, ws: w.id, ...now, latest: r.version, ...kitUpdate.compare(now, r.version) });
|
|
7337
|
+
return json(res, { ok: true, ws: w.id, ...now, latest: r.version, source: r.source || 'npm', ...kitUpdate.compare(now, r.version) });
|
|
7338
7338
|
}
|
|
7339
7339
|
if (kitBusy) return json(res, { ok: false, error: kitBusy === 'app' ? 'The workspace is already updating' : 'A kit update is already running' }, 409);
|
|
7340
7340
|
if (p === '/api/kit/update') {
|