create-pmos 0.4.0 → 0.6.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/README.md +149 -4
- package/bin/create-pmos.js +97 -8
- package/package.json +7 -3
- package/payload/AGENTS-HUMAN.md +6 -0
- package/payload/AGENTS.md +62 -3
- package/payload/KIT-VERSION +2 -2
- package/payload/README.md +16 -2
- package/payload/SECURITY.md +59 -0
- package/payload/okf/core/concepts/design-loop.md +6 -3
- package/payload/okf/core/concepts/frontmatter-format.md +75 -0
- package/payload/okf/core/concepts/index.md +3 -0
- package/payload/okf/core/concepts/log.md +13 -0
- package/payload/okf/core/templates/design-brief-template.md +1 -1
- package/payload/okf/core/templates/eval-rubric-template.md +12 -10
- package/payload/okf/core/templates/prd-template.md +6 -0
- package/payload/okf/core/templates/prototype-brief-template.md +45 -21
- package/payload/planning/evals/agent-run-eval-rubric.md +10 -7
- package/payload/scripts/frontier.sh +60 -0
- package/payload/scripts/metrics.py +163 -102
- package/payload/scripts/pmos-log.sh +16 -6
- package/payload/skills/SKILL-FORMAT.md +72 -9
- package/payload/skills/discovery.skill +4 -0
- package/payload/skills/eval-rubric.skill +1 -1
- package/payload/skills/evaluator.skill +65 -20
- package/payload/skills/explain-run.skill +9 -6
- package/payload/skills/explain-surface.skill +156 -0
- package/payload/skills/ingest-repo.skill +4 -3
- package/payload/skills/pmos-router.skill +25 -3
- package/payload/skills/prd.skill +8 -0
- package/payload/skills/prototype.skill +106 -41
- package/payload/skills-native/copywriting/SKILL.md +57 -0
- package/payload/skills-native/discovery/SKILL.md +75 -0
- package/payload/skills-native/eval-rubric/SKILL.md +159 -0
- package/payload/skills-native/evaluator/SKILL.md +182 -0
- package/payload/skills-native/explain-run/SKILL.md +121 -0
- package/payload/skills-native/explain-surface/SKILL.md +158 -0
- package/payload/skills-native/ingest-repo/SKILL.md +120 -0
- package/payload/skills-native/okr/SKILL.md +70 -0
- package/payload/skills-native/pmos-router/SKILL.md +81 -0
- package/payload/skills-native/prd/SKILL.md +117 -0
- package/payload/skills-native/prototype/SKILL.md +147 -0
- package/payload/state/tickets/.gitkeep +0 -0
- package/payload/templates/agent-run-eval-rubric.md +10 -7
- package/payload/templates/ticket-template.md +20 -0
- package/workspace/README.md +112 -0
- package/workspace/REMOTE.md +131 -0
- package/workspace/bin/pmos.js +206 -0
- package/workspace/doctor.js +99 -0
- package/workspace/lib/console-state.js +117 -0
- package/workspace/lib/file-mode-parse.js +58 -0
- package/workspace/lib/harness-parse.js +222 -0
- package/workspace/lib/preview-kind.js +98 -0
- package/workspace/lib/rate-limit.js +26 -0
- package/workspace/lib/remote-access.js +235 -0
- package/workspace/lib/review-gate.js +39 -0
- package/workspace/lib/schedule-logic.js +132 -0
- package/workspace/lib/skill-store.js +143 -0
- package/workspace/lib/surface-contract.js +79 -0
- package/workspace/lib/transcript-read.js +247 -0
- package/workspace/package-lock.json +94 -0
- package/workspace/package.json +23 -0
- package/workspace/public/app.js +1710 -0
- package/workspace/public/index.html +175 -0
- package/workspace/public/styles.css +766 -0
- package/workspace/server.js +1753 -0
- package/workspace/skills/backlog-triage.skill +13 -0
- package/workspace/skills/weekly-report.skill +19 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
/**
|
|
3
|
+
* transcript-read — the harness, read from the agent CLI's own STRUCTURED record instead of
|
|
4
|
+
* from rendered terminal bytes.
|
|
5
|
+
*
|
|
6
|
+
* WHY THIS EXISTS. The first cut of the harness tower parsed scrollback, and four of six rows
|
|
7
|
+
* in the drill-in came back empty: the CLI does not PRINT its model, effort, token usage or
|
|
8
|
+
* skill set into the terminal. It writes them to a JSONL transcript. So the fix was never a
|
|
9
|
+
* better parser — it was the wrong source. Verified on this machine: 408 assistant records
|
|
10
|
+
* carrying message.model, record-level effort, message.usage token counts, and tool_use names
|
|
11
|
+
* with MCP attribution, plus one skill_listing attachment.
|
|
12
|
+
*
|
|
13
|
+
* ─────────────────────────────────────────────────────────────────────────────────────────
|
|
14
|
+
* VENDOR COUPLING — READ BEFORE CHANGING. Owner: the PM (wawan).
|
|
15
|
+
*
|
|
16
|
+
* This reads an UNDOCUMENTED internal file layout with no compatibility promise:
|
|
17
|
+
* ~/.claude/sessions/<pid>.json → {pid, sessionId, cwd, version, …}
|
|
18
|
+
* ~/.claude/projects/<cwd-slug>/<sessionId>.jsonl
|
|
19
|
+
* The registry file is version-stamped (seen: 2.1.220). When a CLI upgrade moves or reshapes
|
|
20
|
+
* either file, every signal here returns its FAILURE STATE — which is correct behaviour, not a
|
|
21
|
+
* bug to paper over. What to check: (1) does ~/.claude/sessions/<pid>.json still exist and
|
|
22
|
+
* carry sessionId + cwd? (2) is the transcript still <cwd-with-slashes-as-dashes>/<sessionId>.jsonl?
|
|
23
|
+
* (3) do assistant records still carry message.model / message.usage / content[].tool_use?
|
|
24
|
+
*
|
|
25
|
+
* THE RULE THAT MAKES THIS SAFE: no guessing, ever. In particular we NEVER fall back to
|
|
26
|
+
* "the newest .jsonl in this project directory" when the pid→session lookup misses. That
|
|
27
|
+
* fallback would silently render session B's model, tokens and tool counts on session A's row,
|
|
28
|
+
* and two claude sessions in one repo is the NORMAL case here — every session is confined to
|
|
29
|
+
* its workspace root. A miss renders "can't identify this session's transcript" and stops.
|
|
30
|
+
*
|
|
31
|
+
* THE ONE HOP WE DO TOLERATE (child-walk): the workspace's default pane is a SHELL, and the PM
|
|
32
|
+
* types `claude` INSIDE it, so the real agent is a DESCENDANT of the pane pid, which itself has
|
|
33
|
+
* no session record. When — and only when — the pane pid has no record of its own, we descend a
|
|
34
|
+
* BOUNDED number of process levels for a descendant that resolves, accepting it ONLY if its own
|
|
35
|
+
* registry cwd sits inside this workspace root. That is the SAME isolation proof the pane path
|
|
36
|
+
* makes, never a weaker one: identity still flows pid → session record → cwd-in-root. A
|
|
37
|
+
* descendant whose cwd escapes root is refused, not shown. This is emphatically NOT the
|
|
38
|
+
* "newest .jsonl" guess above — we still prove the identity of the pid we resolve.
|
|
39
|
+
*
|
|
40
|
+
* Read-only. Never writes to the PTY or to any CLI state (D62 — the harness runs untouched);
|
|
41
|
+
* the child-walk only reads a `ps` snapshot and session-record files.
|
|
42
|
+
* ─────────────────────────────────────────────────────────────────────────────────────────
|
|
43
|
+
*/
|
|
44
|
+
const fs = require('fs');
|
|
45
|
+
const os = require('os');
|
|
46
|
+
const path = require('path');
|
|
47
|
+
const cp = require('child_process');
|
|
48
|
+
|
|
49
|
+
// The child-walk is bounded so a deep shell nest can never stall a broadcast; past this depth the
|
|
50
|
+
// refusal stands (an honest floor, not a silent truncation — the pane pid still had no record).
|
|
51
|
+
const MAX_WALK_DEPTH = 2;
|
|
52
|
+
|
|
53
|
+
const HOME = () => process.env.PMOS_CLAUDE_HOME || path.join(os.homedir(), '.claude');
|
|
54
|
+
// A transcript can grow large; cap what we read so a long session cannot stall a broadcast.
|
|
55
|
+
const MAX_BYTES = 8 * 1024 * 1024;
|
|
56
|
+
// Synthetic assistant records are the CLI's own bookkeeping, not model turns — never counted,
|
|
57
|
+
// and never a source for the model name (they carry the literal '<synthetic>').
|
|
58
|
+
const SYNTHETIC = '<synthetic>';
|
|
59
|
+
|
|
60
|
+
/** cwd → the CLI's project-directory slug. '/home/user/pmos' → '-home-user-pmos'. */
|
|
61
|
+
function slugFor(cwd) {
|
|
62
|
+
return String(cwd || '').replace(/\//g, '-');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Default descendant lister: ONE `ps` snapshot → a ppid→children map → the direct children of
|
|
67
|
+
* `pid`. Injected in tests so the walk is exercised without real processes. Returns [] on any
|
|
68
|
+
* failure — a resolver miss must degrade to the refusal, never throw.
|
|
69
|
+
*/
|
|
70
|
+
function defaultChildren(pid) {
|
|
71
|
+
let out;
|
|
72
|
+
try {
|
|
73
|
+
out = cp.execFileSync('ps', ['-Ao', 'pid=,ppid='], { encoding: 'utf8', timeout: 2000 });
|
|
74
|
+
} catch {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
const kids = new Map();
|
|
78
|
+
for (const line of out.split('\n')) {
|
|
79
|
+
const m = /^\s*(\d+)\s+(\d+)\s*$/.exec(line);
|
|
80
|
+
if (!m) continue;
|
|
81
|
+
const p = Number(m[1]);
|
|
82
|
+
const pp = Number(m[2]);
|
|
83
|
+
if (!kids.has(pp)) kids.set(pp, []);
|
|
84
|
+
kids.get(pp).push(p);
|
|
85
|
+
}
|
|
86
|
+
return kids.get(Number(pid)) || [];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Breadth-first descendants of `pid`, bounded to `maxDepth` levels. `listChildren(pid)->pids`. */
|
|
90
|
+
function descendants(pid, listChildren, maxDepth = MAX_WALK_DEPTH) {
|
|
91
|
+
const seen = new Set([Number(pid)]);
|
|
92
|
+
let frontier = [Number(pid)];
|
|
93
|
+
const found = [];
|
|
94
|
+
for (let d = 0; d < maxDepth && frontier.length; d++) {
|
|
95
|
+
const next = [];
|
|
96
|
+
for (const p of frontier) {
|
|
97
|
+
for (const c of listChildren(p) || []) {
|
|
98
|
+
const cn = Number(c);
|
|
99
|
+
if (seen.has(cn)) continue;
|
|
100
|
+
seen.add(cn);
|
|
101
|
+
next.push(cn);
|
|
102
|
+
found.push(cn);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
frontier = next;
|
|
106
|
+
}
|
|
107
|
+
return found;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Resolve ONE pid to its validated transcript. Returns {sessionId, file, version} on success, or a
|
|
112
|
+
* typed miss: {noRecord} (no session file for this pid — the shell case), {unreadable}, {outside}
|
|
113
|
+
* (cwd escapes root — the isolation refusal), or {noTranscript} (record exists, transcript not yet
|
|
114
|
+
* written). The cwd-in-root proof here is what makes the child-walk safe: it runs on EVERY pid we
|
|
115
|
+
* consider, pane or descendant, identically.
|
|
116
|
+
*/
|
|
117
|
+
function resolvePid(pid, root, home) {
|
|
118
|
+
let reg;
|
|
119
|
+
try {
|
|
120
|
+
reg = JSON.parse(fs.readFileSync(path.join(home, 'sessions', `${pid}.json`), 'utf8'));
|
|
121
|
+
} catch {
|
|
122
|
+
return { noRecord: true };
|
|
123
|
+
}
|
|
124
|
+
if (!reg || !reg.sessionId || !reg.cwd) return { unreadable: true };
|
|
125
|
+
const rel = path.relative(root, reg.cwd);
|
|
126
|
+
if (root && (rel.startsWith('..') || path.isAbsolute(rel))) return { outside: true };
|
|
127
|
+
const file = path.join(home, 'projects', slugFor(reg.cwd), `${reg.sessionId}.jsonl`);
|
|
128
|
+
if (!fs.existsSync(file)) return { noTranscript: true };
|
|
129
|
+
return { sessionId: reg.sessionId, file, version: reg.version || null };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Map a live process to its transcript. Returns {sessionId, file, version} or {missing}.
|
|
134
|
+
* `root` is the workspace root the session is confined to: the registry's own cwd must sit
|
|
135
|
+
* inside it, or we refuse — that is the isolation check, not a formality. When the pane pid has
|
|
136
|
+
* no record of its own (a SHELL the PM launched claude inside), descend a bounded number of
|
|
137
|
+
* process levels for a descendant that resolves in-root — see the child-walk note at the head.
|
|
138
|
+
* `listChildren` is injectable for tests; it defaults to a real `ps` snapshot.
|
|
139
|
+
*/
|
|
140
|
+
function findTranscript(pid, root, home = HOME(), listChildren = defaultChildren) {
|
|
141
|
+
if (!pid) return { missing: "can't identify this session's transcript — no process id" };
|
|
142
|
+
const direct = resolvePid(pid, root, home);
|
|
143
|
+
if (direct.sessionId) return direct;
|
|
144
|
+
// Only a pane with NO record of its own triggers the walk — that is precisely the shell-wrapped
|
|
145
|
+
// case. A pane that HAS a record (outside root, unreadable, or transcript-not-yet-written) is a
|
|
146
|
+
// real, identified session; we return its honest state rather than second-guessing it downward.
|
|
147
|
+
if (direct.noRecord) {
|
|
148
|
+
for (const child of descendants(pid, listChildren)) {
|
|
149
|
+
const r = resolvePid(child, root, home);
|
|
150
|
+
if (r.sessionId) return r; // first in-root descendant with a real transcript wins
|
|
151
|
+
}
|
|
152
|
+
return { missing: "can't identify this session's transcript — the CLI wrote no session record" };
|
|
153
|
+
}
|
|
154
|
+
if (direct.outside) return { missing: "can't identify this session's transcript — it belongs to another workspace" };
|
|
155
|
+
if (direct.unreadable) return { missing: "can't identify this session's transcript — the session record is unreadable" };
|
|
156
|
+
return { missing: "can't read the harness — the CLI has written no transcript yet" };
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/** Turn 'mcp__github__pull_request_read' into 'github/pull_request_read'. */
|
|
160
|
+
function prettyTool(name) {
|
|
161
|
+
const m = /^mcp__(.+?)__(.+)$/.exec(String(name || ''));
|
|
162
|
+
return m ? `${m[1]}/${m[2]}` : String(name || '');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Read the harness out of a transcript. `clean` is server.js's scrub() — every string that can
|
|
167
|
+
* reach the screen goes through it (D08), because tool names and skill names are free text.
|
|
168
|
+
*/
|
|
169
|
+
function readHarness(file, clean = (x) => x) {
|
|
170
|
+
const out = { model: null, effort: null, turns: 0, tokens: null, tools: null, skills: null, missing: {} };
|
|
171
|
+
let raw;
|
|
172
|
+
try {
|
|
173
|
+
const st = fs.statSync(file);
|
|
174
|
+
const fd = fs.openSync(file, 'r');
|
|
175
|
+
try {
|
|
176
|
+
const start = Math.max(0, st.size - MAX_BYTES);
|
|
177
|
+
const len = st.size - start;
|
|
178
|
+
const buf = Buffer.allocUnsafe(len);
|
|
179
|
+
fs.readSync(fd, buf, 0, len, start);
|
|
180
|
+
raw = buf.toString('utf8');
|
|
181
|
+
if (start > 0) raw = raw.slice(raw.indexOf('\n') + 1); // drop a partial first line
|
|
182
|
+
} finally { fs.closeSync(fd); }
|
|
183
|
+
} catch {
|
|
184
|
+
out.missing.all = "can't read the harness — the transcript could not be read";
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const toolCounts = {};
|
|
189
|
+
let toolTotal = 0;
|
|
190
|
+
const tok = { input: 0, output: 0, cacheRead: 0, cacheCreate: 0 };
|
|
191
|
+
let sawUsage = false;
|
|
192
|
+
|
|
193
|
+
for (const line of raw.split('\n')) {
|
|
194
|
+
if (!line) continue;
|
|
195
|
+
let r;
|
|
196
|
+
try { r = JSON.parse(line); } catch { continue; } // a torn line is skipped, never guessed
|
|
197
|
+
if (r.type === 'attachment') {
|
|
198
|
+
const a = r.attachment || {};
|
|
199
|
+
if (a.type === 'skill_listing' && typeof a.skillCount === 'number') {
|
|
200
|
+
out.skills = {
|
|
201
|
+
count: a.skillCount,
|
|
202
|
+
names: Array.isArray(a.names) ? a.names.slice(0, 40).map((n) => clean(String(n))) : [],
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
if (r.type !== 'assistant') continue;
|
|
208
|
+
const m = r.message || {};
|
|
209
|
+
if (m.model && m.model !== SYNTHETIC) {
|
|
210
|
+
out.model = clean(m.model);
|
|
211
|
+
out.turns++; // only real model turns count
|
|
212
|
+
if (r.effort) out.effort = clean(String(r.effort));
|
|
213
|
+
}
|
|
214
|
+
const u = m.usage || {};
|
|
215
|
+
if (typeof u.input_tokens === 'number' || typeof u.output_tokens === 'number') {
|
|
216
|
+
sawUsage = true;
|
|
217
|
+
tok.input += u.input_tokens || 0;
|
|
218
|
+
tok.output += u.output_tokens || 0;
|
|
219
|
+
tok.cacheRead += u.cache_read_input_tokens || 0;
|
|
220
|
+
tok.cacheCreate += u.cache_creation_input_tokens || 0;
|
|
221
|
+
}
|
|
222
|
+
for (const c of (m.content || [])) {
|
|
223
|
+
if (c && c.type === 'tool_use' && c.name) {
|
|
224
|
+
const name = clean(prettyTool(c.name));
|
|
225
|
+
toolCounts[name] = (toolCounts[name] || 0) + 1;
|
|
226
|
+
toolTotal++;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (!out.model) out.missing.model = 'the transcript names no model yet';
|
|
232
|
+
if (!out.effort) out.missing.effort = 'the transcript records no effort level';
|
|
233
|
+
if (toolTotal) out.tools = { counts: toolCounts, total: toolTotal };
|
|
234
|
+
else out.missing.tools = 'no tool calls in the transcript yet';
|
|
235
|
+
// Tokens are reported as ABSOLUTE counts and never as a percentage: the context window size
|
|
236
|
+
// is not in the record, and autocompaction makes the running total move backward. A percentage
|
|
237
|
+
// of an unknown denominator is not a quantity — that refusal predates this module and stands.
|
|
238
|
+
if (sawUsage) out.tokens = tok;
|
|
239
|
+
else out.missing.tokens = 'the transcript records no token usage yet';
|
|
240
|
+
if (!out.skills) out.missing.skills = 'the CLI listed no skills for this session';
|
|
241
|
+
if (!out.turns) out.missing.turns = 'no model turns in the transcript yet';
|
|
242
|
+
|
|
243
|
+
return out;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
module.exports = { findTranscript, readHarness, prettyTool, slugFor, resolvePid, descendants,
|
|
247
|
+
MAX_BYTES, MAX_WALK_DEPTH };
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pmos-workspace",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"lockfileVersion": 3,
|
|
5
|
+
"requires": true,
|
|
6
|
+
"packages": {
|
|
7
|
+
"": {
|
|
8
|
+
"name": "pmos-workspace",
|
|
9
|
+
"version": "0.1.0",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"dependencies": {
|
|
12
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
13
|
+
"@xterm/addon-web-links": "^0.12.0",
|
|
14
|
+
"@xterm/xterm": "^5.5.0",
|
|
15
|
+
"node-pty": "^1.0.0",
|
|
16
|
+
"playwright-core": "^1.61.1",
|
|
17
|
+
"ws": "^8.18.0"
|
|
18
|
+
},
|
|
19
|
+
"bin": {
|
|
20
|
+
"pmos": "bin/pmos.js"
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=18"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"node_modules/@xterm/addon-fit": {
|
|
27
|
+
"version": "0.11.0",
|
|
28
|
+
"resolved": "https://registry.npmjs.org/@xterm/addon-fit/-/addon-fit-0.11.0.tgz",
|
|
29
|
+
"integrity": "sha512-jYcgT6xtVYhnhgxh3QgYDnnNMYTcf8ElbxxFzX0IZo+vabQqSPAjC3c1wJrKB5E19VwQei89QCiZZP86DCPF7g==",
|
|
30
|
+
"license": "MIT"
|
|
31
|
+
},
|
|
32
|
+
"node_modules/@xterm/addon-web-links": {
|
|
33
|
+
"version": "0.12.0",
|
|
34
|
+
"resolved": "https://registry.npmjs.org/@xterm/addon-web-links/-/addon-web-links-0.12.0.tgz",
|
|
35
|
+
"integrity": "sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==",
|
|
36
|
+
"license": "MIT"
|
|
37
|
+
},
|
|
38
|
+
"node_modules/@xterm/xterm": {
|
|
39
|
+
"version": "5.5.0",
|
|
40
|
+
"resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz",
|
|
41
|
+
"integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==",
|
|
42
|
+
"license": "MIT"
|
|
43
|
+
},
|
|
44
|
+
"node_modules/node-addon-api": {
|
|
45
|
+
"version": "7.1.1",
|
|
46
|
+
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
|
|
47
|
+
"integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
|
|
48
|
+
"license": "MIT"
|
|
49
|
+
},
|
|
50
|
+
"node_modules/node-pty": {
|
|
51
|
+
"version": "1.1.0",
|
|
52
|
+
"resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz",
|
|
53
|
+
"integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==",
|
|
54
|
+
"hasInstallScript": true,
|
|
55
|
+
"license": "MIT",
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"node-addon-api": "^7.1.0"
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"node_modules/playwright-core": {
|
|
61
|
+
"version": "1.61.1",
|
|
62
|
+
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz",
|
|
63
|
+
"integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==",
|
|
64
|
+
"license": "Apache-2.0",
|
|
65
|
+
"bin": {
|
|
66
|
+
"playwright-core": "cli.js"
|
|
67
|
+
},
|
|
68
|
+
"engines": {
|
|
69
|
+
"node": ">=18"
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
"node_modules/ws": {
|
|
73
|
+
"version": "8.21.1",
|
|
74
|
+
"resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz",
|
|
75
|
+
"integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==",
|
|
76
|
+
"license": "MIT",
|
|
77
|
+
"engines": {
|
|
78
|
+
"node": ">=10.0.0"
|
|
79
|
+
},
|
|
80
|
+
"peerDependencies": {
|
|
81
|
+
"bufferutil": "^4.0.1",
|
|
82
|
+
"utf-8-validate": ">=5.0.2"
|
|
83
|
+
},
|
|
84
|
+
"peerDependenciesMeta": {
|
|
85
|
+
"bufferutil": {
|
|
86
|
+
"optional": true
|
|
87
|
+
},
|
|
88
|
+
"utf-8-validate": {
|
|
89
|
+
"optional": true
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "pmos-workspace",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "PMOS Terminal Workspace — local companion hosting real PTYs beside the library and gates (D62)",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"bin": { "pmos": "bin/pmos.js" },
|
|
8
|
+
"scripts": {
|
|
9
|
+
"start": "node server.js",
|
|
10
|
+
"test": "node test.js",
|
|
11
|
+
"test:ui": "node ui-test.js",
|
|
12
|
+
"doctor": "node doctor.js"
|
|
13
|
+
},
|
|
14
|
+
"engines": { "node": ">=18" },
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@xterm/addon-fit": "^0.11.0",
|
|
17
|
+
"@xterm/addon-web-links": "^0.12.0",
|
|
18
|
+
"@xterm/xterm": "^5.5.0",
|
|
19
|
+
"node-pty": "^1.0.0",
|
|
20
|
+
"playwright-core": "^1.61.1",
|
|
21
|
+
"ws": "^8.18.0"
|
|
22
|
+
}
|
|
23
|
+
}
|