create-pmos 0.166.0 → 0.167.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-pmos",
3
- "version": "0.166.0",
3
+ "version": "0.167.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": {
@@ -1,3 +1,3 @@
1
- kernel-source: pmos@4ae0213
1
+ kernel-source: pmos@23fe058
2
2
  assembled: 2026-10-12
3
3
  mode: vendor
@@ -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-12T13:44:56Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-12T14:00:51Z"}
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-12T13:44:56Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-12T14:00:51Z"}
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-12T13:44:56Z"}
5
+ generated: {by: process:create-pmos, at: "2026-10-12T14:00:51Z"}
6
6
  status: stable
7
7
  ---
8
8
 
@@ -112,7 +112,7 @@ The workspace reads the TypeSafe key only on this machine. After you save the ke
112
112
  If you have no TypeSafe key, a model on your computer can answer the same questions. Two models are supported. Both use the same interface as Jev.
113
113
 
114
114
  - **Winnow**: in a `winnow-inference` folder, run `python3 scripts/serve.py`. It uses port 8091. Winnow-12B needs about 16 GB of memory.
115
- - **Laya**: run `pip install "laya[serve]"`, then `laya-serve`. It uses port 8000. Laya is small and runs on a laptop.
115
+ - **Laya**: in Settings, press **Set up Laya**. A terminal named Set up Laya opens with the commands typed in. Read them and press Enter. The first time, they install Laya into the PMOS data folder. Then Laya starts and uses port 8000. Laya is small and runs on a laptop. Laya stops when you close that terminal or restart the computer. Press **Set up Laya** again to start it.
116
116
 
117
117
  1. Start the model.
118
118
  2. Open Settings, then **Privacy and data**. Under **Local model if Jev is missing**, press **Winnow** or **Laya**.
@@ -90,6 +90,20 @@ function status(stateDir, env = null) {
90
90
  return { present: false, source: null, local: null };
91
91
  }
92
92
 
93
+ /** The commands that install Laya into its own folder under the runtime directory (once) and start it on loopback.
94
+ * The workspace types them into a terminal and never runs them itself: the PM reads them and presses Enter (as D91's
95
+ * Whisper setup does). A folder of its own avoids the system Python, which newer Macs refuse to install into. */
96
+ function layaSetupCommand(stateDir, platform = process.platform) {
97
+ const dir = path.join(stateDir, 'laya');
98
+ const port = typesafe.BACKENDS.laya.port;
99
+ if (platform === 'win32') {
100
+ return `if (-not (Test-Path "${dir}\\Scripts\\laya-serve.exe")) { python -m venv "${dir}"; & "${dir}\\Scripts\\python" -m pip install "laya[serve]" }; `
101
+ + `$env:LAYA_HOST="127.0.0.1"; $env:LAYA_PORT="${port}"; $env:LAYA_DEVICE="cpu"; $env:LAYA_PRELOAD="1"; & "${dir}\\Scripts\\laya-serve"`;
102
+ }
103
+ return `[ -x "${dir}/bin/laya-serve" ] || { python3 -m venv "${dir}" && "${dir}/bin/python" -m pip install "laya[serve]"; } && `
104
+ + `LAYA_HOST=127.0.0.1 LAYA_PORT=${port} LAYA_DEVICE=cpu LAYA_PRELOAD=1 "${dir}/bin/laya-serve"`;
105
+ }
106
+
93
107
  /** Save or clear the local fallback server. `backend` is 'winnow' or 'laya'; `port` defaults to that server's own. */
94
108
  function setLocal(stateDir, { backend, port } = {}) {
95
109
  const cur = read(stateDir);
@@ -163,4 +177,4 @@ function isExcluded(exclude, rel) {
163
177
  });
164
178
  }
165
179
 
166
- module.exports = { adoptEnv, status, getKey, setKey, clearKey, setLocal, getRepo, setRepo, isExcluded, ENV_NAME, FILE };
180
+ module.exports = { adoptEnv, status, getKey, setKey, clearKey, setLocal, layaSetupCommand, getRepo, setRepo, isExcluded, ENV_NAME, FILE };
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.166.0",
3
+ "version": "0.167.0",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "pmos-workspace",
9
- "version": "0.166.0",
9
+ "version": "0.167.0",
10
10
  "license": "MIT",
11
11
  "dependencies": {
12
12
  "@xterm/addon-fit": "^0.11.0",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pmos-workspace",
3
- "version": "0.166.0",
3
+ "version": "0.167.0",
4
4
  "private": true,
5
5
  "description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
6
6
  "license": "MIT",
@@ -618,14 +618,24 @@ export function mountSettings(host, ctx = {}) {
618
618
  M.localTest = r && r.ok ? { ok: true, text: 'Answered (' + (r.model || r.local) + ').' } : { ok: false, text: (r && r.error) || 'No answer.' };
619
619
  draw();
620
620
  };
621
+ const setup = async () => {
622
+ M.localTest = { busy: true }; draw();
623
+ const r = await pmPost('/api/typesafe/local/setup', {});
624
+ if (r && r.ok) {
625
+ M.typesafe = r.key; delete M.err['typesafe.local'];
626
+ M.localTest = { ok: !!r.started, text: r.started ? 'A terminal named Set up Laya opened with the commands typed in. Press Enter there, wait until Laya says it is running, then press Test.' : 'No terminal opened (' + (r.reason || 'unknown') + '). Run this in a terminal: ' + r.command };
627
+ } else { M.localTest = null; M.err['typesafe.local'] = (r && r.error) || 'Not started.'; }
628
+ draw();
629
+ };
621
630
  const lt = M.localTest;
622
631
  const note = !loc ? 'Off. With no TypeSafe key, Jev features stay off.'
623
632
  : (t.source === 'local' ? 'No TypeSafe key, so ' + loc.name + ' answers every Jev question.' : 'TypeSafe answers first; ' + loc.name + ' answers when it fails.')
624
633
  + ' PMOS calls 127.0.0.1:' + loc.port + ' only. Nothing leaves this computer.' + (lt && !lt.busy ? ' ' + lt.text : '');
625
- return frame({ key: 'typesafe.local', label: 'Local model if Jev is missing', help: 'Start Winnow (winnow-inference, port 8091) or Laya (laya-serve, port 8000) on this computer, then pick it here.', scope: 'computer',
634
+ return frame({ key: 'typesafe.local', label: 'Local model if Jev is missing', help: 'Press Set up Laya to install and start Laya in a terminal, or start Winnow (winnow-inference, port 8091) yourself and pick it here.', scope: 'computer',
626
635
  err: M.err['typesafe.local'], note,
627
636
  control: t ? el('div', { class: 'iacts' }, seg('Local model', [['off', 'Off'], ['winnow', 'Winnow'], ['laya', 'Laya']], cur, pick),
628
- loc ? btn(lt && lt.busy ? 'Testing' : 'Test', { variant: 'ghost', disabled: !!(lt && lt.busy), onclick: test }) : null) : tag('Reading') });
637
+ btn('Set up Laya', { variant: 'ghost', disabled: !!(lt && lt.busy), onclick: setup, attrs: { 'data-c3set-laya-setup': '' } }),
638
+ loc ? btn(lt && lt.busy ? 'Working' : 'Test', { variant: 'ghost', disabled: !!(lt && lt.busy), onclick: test }) : null) : tag('Reading') });
629
639
  }
630
640
 
631
641
  function appearanceSec() {
@@ -7976,6 +7976,17 @@ async function handleRequest(req, res) {
7976
7976
  if (!out.ok) return json(res, { ok: false, error: out.error }, /choose|port/.test(out.error) ? 400 : 500);
7977
7977
  return json(res, { ok: true, key: tsSettings.status(STATE) });
7978
7978
  }
7979
+ /* Set up Laya: picks Laya as the local model and opens a terminal with the install-and-start commands typed in, not run.
7980
+ * The PM reads them and presses Enter; Laya then runs in that terminal on 127.0.0.1:8000 (D95). */
7981
+ if (p === '/api/typesafe/local/setup' && req.method === 'POST') {
7982
+ const saved = tsSettings.setLocal(STATE, { backend: 'laya' });
7983
+ if (!saved.ok) return json(res, { ok: false, error: saved.error }, 500);
7984
+ const command = tsSettings.layaSetupCommand(STATE);
7985
+ const home = homeWorkspace();
7986
+ const s = spawnSession({ name: 'Set up Laya', agent: 'shell', ws: home ? home.id : undefined, origin: 'laya-setup', initialInput: command });
7987
+ const started = !!s && s.state !== 'exited';
7988
+ return json(res, { ok: true, started, session: s ? s.id : null, command, key: tsSettings.status(STATE), reason: started ? '' : ((s && s.note) || 'could not start a terminal') });
7989
+ }
7979
7990
  if (p === '/api/typesafe/local/test' && req.method === 'POST') {
7980
7991
  if (!tsSettings.status(STATE).local) return json(res, { ok: false, error: 'No local model is set.' }, 409);
7981
7992
  const r = await tsClient.ask({ key: tsClient.LOCAL_KEY, state: 'PMOS is checking that the local model answers.', retries: 0,