diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 00000000..7af2823e --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,16 @@ +{ + "name": "agent360", + "owner": { "name": "Agent360", "url": "https://computermcp.dev" }, + "description": "Computer MCP: macOS computer use you can leave running.", + "plugins": [ + { + "name": "computer-mcp", + "source": "./plugin", + "description": "macOS computer use that stays behind your window, asks before it deletes or sends, refuses password managers, and logs every action.", + "version": "0.1.0", + "homepage": "https://computermcp.dev", + "repository": "https://github.com/Agent360dk/computerMCP", + "license": "MIT" + } + ] +} diff --git a/.github/ISSUE_TEMPLATE/bug.yml b/.github/ISSUE_TEMPLATE/bug.yml new file mode 100644 index 00000000..f8f70ec4 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug.yml @@ -0,0 +1,35 @@ +name: "Something did not work" +description: "An agent tried something on your Mac and it failed, or it took your screen when it should not have." +title: "Bug: " +labels: ["bug"] +body: + - type: markdown + attributes: + value: | + Nothing is sent from your Mac on its own. Paste only what you are happy to share: + the audit log hashes typed text and never stores what was on screen, but app names + and window titles are in it. + - type: textarea + id: what + attributes: + label: What the agent tried, and what actually happened + validations: + required: true + - type: textarea + id: audit + attributes: + label: The last few lines of the audit log + description: "Ask the agent for `computer_audit` with limit 10, or copy from ~/.local/state/computer-mcp/audit.jsonl." + render: json + - type: textarea + id: learning + attributes: + label: Anything it wrote with computer_learning + description: "~/.local/state/computer-mcp/learnings.jsonl - numbers and email addresses are already masked." + - type: input + id: versions + attributes: + label: macOS version and computer-mcp version + placeholder: "macOS 26.4.1 · computer-mcp 0.2.2" + validations: + required: true diff --git a/.github/mutant-giv-tilbage.sh b/.github/mutant-giv-tilbage.sh index a181830d..d0972378 100644 --- a/.github/mutant-giv-tilbage.sh +++ b/.github/mutant-giv-tilbage.sh @@ -5,28 +5,71 @@ set -u fejl=0 mut() { - local navn="$1" fra="$2" til="$3" d="$RUNNER_TEMP/mut-$1" + local navn="$1" fra="$2" til="$3" d="$RUNNER_TEMP/mut-$1" maal="${MUT_MAAL:-cmcp-helper}" # Uden byggemappen: den baerer faste stier til originalen (koersel 3 kunne ikke bygge). mkdir -p "$d" && cp -R helper/Package.swift helper/Sources "$d"/ [ -f helper/Package.resolved ] && cp helper/Package.resolved "$d"/ - python3 - "$d/Sources/cmcp-helper/${MUT_FIL:-Skaerm.swift}" "$fra" "$til" <<'PY' + # ⛔ 30/9 (Astra R2 7): et anker der ikke fandtes, stoppede ikke noget - scriptet + # byggede den UMUTEREDE kode og kaldte resultatet et bevis. Nu er det en fejl. + if ! python3 - "$d/Sources/$maal/${MUT_FIL:-Skaerm.swift}" "$fra" "$til" <<'PY2' import sys p, a, b = sys.argv[1:4] s = open(p).read() assert s.count(a) == 1, a open(p, 'w').write(s.replace(a, b)) -PY - (cd "$d" && swift build -c release 2>&1 | tail -5) ; [ -x "$d/.build/release/cmcp-helper" ] || { echo "::error::$navn kunne ikke bygges"; fejl=1; return; } - if CMCP_HELPER="$d/.build/release/cmcp-helper" node test/giv-tilbage.mjs; then - echo "::error::mutanten $navn overlevede - proeven maaler ikke"; fejl=1 - else - echo "mutanten $navn er roed" - fi +PY2 + then echo "::error::$navn: ankeret findes ikke praecis én gang"; fejl=1; return; fi + (cd "$d" && swift build -c release 2>&1 | tail -5) ; [ -x "$d/.build/release/$maal" ] || { echo "::error::$navn kunne ikke bygges"; fejl=1; return; } + local ud rc + ud=$(CMCP_HELPER="$d/.build/release/cmcp-helper" CMCP_STATUS_BIN="$d/.build/release/cmcp-status" node "${PROEVE:-test/giv-tilbage.mjs}" 2>&1); rc=$? + # Roed = exit 1 OG en DUMP-linje. Exit 0 = overlevede. Alt andet = instrumentet svarede ikke. + if [ $rc -eq 0 ]; then echo "::error::mutanten $navn overlevede - proeven maaler ikke"; fejl=1 + # Runde 3 (Astra 6): KUN den prove mutanten er skrevet til taeller (FORVENTET), ikke en vilkaarlig anden. + # Runde 5 (Astra): hele det forventede praefiks skal staa FORREST paa en DUMP-linje. + elif [ $rc -eq 1 ] && printf '%s\n' "$ud" | awk -v p="DUMP ${FORVENTET:-}" 'index($0, p) == 1 { f = 1 } END { exit !f }'; then echo "mutanten $navn er roed ($(printf '%s\n' "$ud" | awk -v p="DUMP ${FORVENTET:-}" 'index($0, p) == 1 { print; exit }' | cut -c1-90))" + else echo "::error::$navn: instrumentet svarede ikke (rc=$rc) - det er ikke et bevis"; fejl=1; fi } -mut M1-giver-ikke-tilbage 'NSRunningApplication(processIdentifier: foer.forrestPid)?.activate(options: [])' '' -mut M2-maaler-ikke 'guard foer.forrestPid > 0, efter.forrestPid != foer.forrestPid else { return ["took_screen": false] }' 'return ["took_screen": false]' +FORVENTET=2b mut M1-giver-ikke-tilbage 'NSRunningApplication(processIdentifier: foer.forrestPid)?.activate(options: [])' '' +FORVENTET='2 et tryk' mut M2-maaler-ikke 'guard foer.forrestPid > 0, efter.forrestPid != foer.forrestPid else { return ["took_screen": false] }' 'return ["took_screen": false]' # M3: launch giver ikke forgrunden tilbage (main.swift) -MUT_FIL=main.swift mut M3-launch-giver-ikke-tilbage 'if (g["took_screen"] as? Bool) == true { for (k, v) in g { ls[k] = v } }' 'if false { for (k, v) in g { ls[k] = v } }' -# M4: kun et skift til maalprogrammet gives tilbage (reglen foer koersel 10) -mut M4-kun-maalprogrammet 'efter.forrestPid == maal || !menneskeRoerteNetop()' 'efter.forrestPid == maal' +FORVENTET='3 et program' MUT_FIL=main.swift mut M3-launch-giver-ikke-tilbage 'if (g["took_screen"] as? Bool) == true || g["observed"] != nil { for (k, v) in g { ls[k] = v } }' 'if false { for (k, v) in g { ls[k] = v } }' +# M4 (28/9): menneske-tjekket maa ikke spurioest blokere give-tilbage. Paa en +# maskine UDEN menneske skal forgrunden stadig gives tilbage - saa hvis nogen +# faar menneske-grenen til altid at fyre (og dermed altid lade forgrunden staa), +# SKAL proeven blive roed. +FORVENTET=2b mut M4-menneske-tjek-blokerer 'if menneskeRoerteNetop() {' 'if true {' +# M5-M6 (29/9, panelet): kodeordsfelter faar heller ikke tastetryk fra `type`. +# M5: fokus-opslaget siger altid «ikke sikkert» -> proeve 3/3k/3b skal blive roed. +# M6: tjekket kun foer foerste tegn -> fokus der flytter undervejs (3c) skal blive roed. +FORVENTET='3 et kodeordsfelt' PROEVE=test/skriv-ankommer.mjs MUT_FIL=Accessibility.swift mut M5-type-i-kodeordsfelt \ + 'static func sikkerStatus(_ el: AXUIElement, kendtRolle: String? = nil) -> Bool? {' \ + 'static func sikkerStatus(_ el: AXUIElement, kendtRolle: String? = nil) -> Bool? { + if true { return false }' +FORVENTET=3c PROEVE=test/skriv-ankommer.mjs MUT_FIL=Input.swift mut M6-kun-tjek-ved-start \ + 'if let stop, stop() { return sendt }' 'if let stop, sendt == 0, stop() { return sendt }' +# M7-M8 (30/9, runde 5): paste stopper foer Cmd+V naar laanet slutter. +# M7: det foerste stop (foer udklipsholderen roeres) er vaek -> «before anything changed» maa ikke kunne naas. +FORVENTET='1 SIGUSR1' PROEVE=test/paste-stop.mjs MUT_FIL=Accessibility.swift mut M7-paste-roerer-udklip-foer-stop \ + ' if pasteStop { return (false, "stopped before anything changed: the screen loan ended", false) }' '' +# M8: stoppet laeses aldrig -> paste fortsaetter til Cmd+V (kun paa en fremmed maskine). +FORVENTET='1 SIGUSR1' PROEVE=test/paste-stop.mjs MUT_FIL=Accessibility.swift mut M8-paste-hoerer-ikke-stop \ + ' return sigismember(&s, SIGUSR1) == 1' ' return false' +# M9 (30/9, live-proeven): boksen naevner ikke det ventende spoergsmaal -> 3c skal blive roed. +# 12/10: ankeret flyttet til boksens titel (Tekst.swift) - det gamle forsvandt i 7bc3f41, og ingen saa det, +# fordi «Fremmed Mac» var annulleret paa hver kandidat siden 9/10 (CI 38230972853 fandt det). +FORVENTET=3c PROEVE=test/ikon-menu.mjs MUT_MAAL=cmcp-status MUT_FIL=Tekst.swift mut M9-boksen-tier-om-spoergsmaalet \ + 'titel: "\(renTekst(v.klient ?? "An agent")) needs you"' 'titel: "Computer MCP"' +# M10 (1/10, konsulent-panelet): giv-tilbage river igen ethvert program tilbage - +# ogsaa et der koerte i forvejen (= mennesket der skiftede skrivebord) -> 4 skal blive roed. +FORVENTET='4 et program der koerte' mut M10-river-menneskets-skift-tilbage \ + 'if !egen {' 'if false {' +# M11 (1/10, GitHubs Mac 36863990504 - retarget 36901238860): almindelig skrivning +# taeller igen som et menneskeligt skift -> et rigtigt tastetryk ville fejlagtigt +# blokere en legitim tilbagegivelse. Oprindeligt bevist via parallel.mjs's +# menneske-simulation, som siden skriver via tilgaengeligheds-laget (ingen rigtig +# keyDown postes der laengere - den mutant blev umaalelig af parallel.mjs selv). +# giv-tilbage.mjs's sag 5 sender et AEGTE globalt tastetryk med vilje og maaler +# praecis dette. +FORVENTET='5 et rigtigt tastetryk' mut M11-skrivning-er-et-skift \ + '[.flagsChanged, .leftMouseDown' '[.keyDown, .flagsChanged, .leftMouseDown' exit $fejl diff --git a/.github/mutant-porte.py b/.github/mutant-porte.py new file mode 100644 index 00000000..e17a659f --- /dev/null +++ b/.github/mutant-porte.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python3 +# Mutationsbevis for portene (29/9, B7 - MANDAT: «hver vagt skal kunne blive roed»). +# Hver mutant i mutanter-porte.json bygger én fejl ind i en vagt; dens proeve SKAL +# blive roed. Filen gendannes ALTID fra en kopi taget lige foer - aldrig fra git, +# for filen kan baere ucommittet arbejde (30/9: `git checkout` tog en hel funktion). +import hashlib, json, os, shutil, subprocess, sys, tempfile +ROD = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) +os.chdir(ROD) +overlevede, fejl = [], [] +MUTANTER = json.load(open('.github/mutanter-porte.json', encoding='utf-8')) +# ⛔ 19/10 (S2b-2, MAALT): en proeve der allerede er ROED uden mutation, faar alle sine mutanter til at se +# «fanget» ud - S19 var roed, og 8 mutanter blev meldt roede uden at vaere proevet. Hver proeve koeres derfor +# foerst UMUTERET; er den ikke groen, er dens mutanter ikke bevist. +groen = {} +for proeve in sorted({m['proeve'] for m in MUTANTER}): + tmp = tempfile.mkdtemp(prefix='cmcp-m-', dir='/tmp') + try: + k = subprocess.run(['node', proeve], capture_output=True, text=True, timeout=600, env={**os.environ, 'TMPDIR': tmp + '/'}) + groen[proeve] = k.returncode == 0 + except subprocess.TimeoutExpired: + groen[proeve] = False + finally: + shutil.rmtree(tmp, ignore_errors=True) + if not groen[proeve]: print(f"::error::proeven {proeve} er ROED uden mutation - dens mutanter er ikke bevist") +for m in MUTANTER: + if not groen.get(m['proeve']): + fejl.append(f"{m['navn']}: proeven {m['proeve']} er roed uden mutation"); continue + orig = open(m['fil'], encoding='utf-8').read() + h = hashlib.sha256(orig.encode()).hexdigest() + par = m.get('par') or [[m['fra'], m['til']]] + tael = [orig.count(a) for a, _ in par] + if any(t != 1 for t in tael): + fejl.append(f"{m['navn']}: ankrene findes {tael} gange"); continue + muteret = orig + for a, b in par: muteret = muteret.replace(a, b) + try: + open(m['fil'], 'w', encoding='utf-8').write(muteret) + # 11/10 (diskchatten): hver mutant koerer i sin EGEN midlertidige mappe, som fjernes bagefter. + # Mutanten faar proeven til at dumpe med vilje - og en dumpet proeve efterlod sine mapper: + # tusindvis pr. port, og disken loeb fuld (Mac'en gik ned 11/10 11.03). + tmp = tempfile.mkdtemp(prefix='cmcp-m-', dir='/tmp') + try: + k = subprocess.run(['node', m['proeve']], capture_output=True, text=True, timeout=600, + env={**os.environ, 'TMPDIR': tmp + '/'}) + rc, ud = k.returncode, k.stdout + except subprocess.TimeoutExpired: + rc, ud = 'timeout', '' + finally: + shutil.rmtree(tmp, ignore_errors=True) + finally: + open(m['fil'], 'w', encoding='utf-8').write(orig) + if hashlib.sha256(open(m['fil'], encoding='utf-8').read().encode()).hexdigest() != h: + fejl.append(f"{m['navn']}: {m['fil']} blev IKKE gendannet"); break + # ⛔ Runde 1 30/9 (Astra 9): enhver ikke-nul exit talte som «roed» - ogsaa en + # prove der crashede for foerste assertion. Roed = rc 1 OG en DUMP-linje. + # ⛔ Runde 2 (Astra 7): en tilfaeldig ANDEN DUMP-linje var ogsaa «bevis». Nu skal + # den prove mutanten er skrevet til (`forventet`), vaere en af de roede. + doemt = [l for l in ud.splitlines() if l.startswith('DUMP ' + m.get('forventet', ''))] + if rc == 0: + overlevede.append(m['navn']); print(f"::error::mutanten {m['navn']} overlevede - {m['proeve']} maaler ikke") + elif rc == 1 and doemt: + print(f"mutanten {m['navn']} er roed ({m['proeve']}: {doemt[0][:90]})") + else: + fejl.append(f"{m['navn']}: instrumentet svarede ikke (rc={rc}, {len(doemt)} DUMP-linjer) - det er ikke et bevis") +for f in fejl: print(f"::error::{f}") +print(f"{len(overlevede)} overlevede, {len(fejl)} fejl") +sys.exit(1 if overlevede or fejl else 0) diff --git a/.github/mutanter-porte.json b/.github/mutanter-porte.json new file mode 100644 index 00000000..6013fe25 --- /dev/null +++ b/.github/mutanter-porte.json @@ -0,0 +1,4854 @@ +[ + { + "navn": "S2-tekst-klippet", + "fil": "mcp-server/godkend.js", + "par": [ + [ + "text: helTekst, scope: kort(scope)", + "text: helTekst.slice(0, 200), scope: kort(scope)" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "A14" + }, + { + "navn": "S2-intet-loft", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " if (n > TEKST_LOFT) return `the action is", + " if (false) return `the action is" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "A15" + }, + { + "navn": "S3-venter-haenger", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " venter = false;\n clearTimeout(ur);", + " clearTimeout(ur);" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "AG" + }, + { + "navn": "S3-buffer-overskrevet", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " buf += d;\n const i = buf.indexOf('\\n');\n if (i < 0) return;\n let m = null;", + " buf = String(d);\n const i = buf.indexOf('\\n');\n if (i < 0) return;\n let m = null;" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "F3" + }, + { + "navn": "S3-lukket-er-ja", + "fil": "mcp-server/godkend.js", + "par": [ + [ + "sock.on('close', () => slut({ ok: false,", + "sock.on('close', () => slut({ ok: true," + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "F1" + }, + { + "navn": "B3-done-overalt", + "fil": "mcp-server/godkend.js", + "par": [ + [ + "m.verified === 'done' && kind === 'goer-selv'", + "m.verified === 'done'" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "E3" + }, + { + "navn": "B3-modellen-skriver-hvor", + "fil": "mcp-server/index.js", + "par": [ + [ + " target: hvorIkon, targetBundle: bid", + " target: String(args.message).slice(0, 80), targetBundle: bid" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "E1" + }, + { + "navn": "R1-boks-efter-nej", + "fil": "mcp-server/policy.js", + "par": [ + [ + " if (!svar.ikkeKoerer) {\n if (svar.ok && nytSessionsProgram", + " if (!svar.ikkeSpurgt) {\n if (svar.ok && nytSessionsProgram" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "G1" + }, + { + "navn": "B4-spoerger-ikke", + "fil": "mcp-server/index.js", + "par": [ + [ + " || !!sende,", + " || false," + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "a send-knappen" + }, + { + "navn": "B4-return-er-ikke-send", + "fil": "mcp-server/policy.js", + "par": [ + [ + " if (tast === 'return' || tast === 'enter') return slags === 'chat' || mods.has('cmd') || mods.has('ctrl');", + "" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "b «return»" + }, + { + "navn": "B4-linjeskift-slipper", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (name === 'computer_type' && slags === 'chat' && /[\\p{Cc}\\p{Zl}\\p{Zp}]/u.test(String(args.text ?? '').replace(/\\t/g, ''))) {", + " if (false) {" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "c linjeskift" + }, + { + "navn": "B4-modellen-skriver-modtager", + "fil": "mcp-server/index.js", + "par": [ + [ + " const hvem = [...(sam.recipient ? [sam.recipient] : (sam.headings || []))]", + " const hvem = [String(args.contains || ''), ...(sam.recipient ? [sam.recipient] : (sam.headings || []))]" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "d en falsk modtager" + }, + { + "navn": "R4-genkontrol-kun-ved-send", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (beskedSlags(targetBundleId)) {\n // R27: et klik med `app` genmaales", + " if (sende?.describe) {\n // R27: et klik med `app` genmaales" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r4" + }, + { + "navn": "R5-svagt-fingeraftryk", + "fil": "mcp-server/index.js", + "par": [ + [ + " const fp = JSON.stringify({ w: sam.window || '', h: sam.headings || [], r: sam.recipient || '', v: felt.value, c: !!sam.column });", + " const fp = JSON.stringify({ v: felt.value });" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r5b" + }, + { + "navn": "R5-ulaeseligt-spoerges", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!hvem.length || !tekst) {", + " if (false) {" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "u ulaeselig" + }, + { + "navn": "R6-navnloes-er-ikke-send", + "fil": "mcp-server/index.js", + "par": [ + [ + " : (!rolle || /^AX(Button|Image|Group|Unknown|Link)$/.test(String(rolle)));", + " : false;" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r6 en navnloes" + }, + { + "navn": "R6-danske-ord", + "fil": "mcp-server/policy.js", + "par": [ + [ + "(send|resend|sende|afsend|indsend|reply|svar|besvar|", + "(send|resend|sende|indsend|reply|svar|" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r6 «Afsend»" + }, + { + "navn": "R6-webchat-overset", + "fil": "mcp-server/index.js", + "par": [ + [ + " slags = titler.some(x => !x || WEBCHAT.test(x)) ? 'chat' : titler.some(x => WEBMAIL.test(x)) ? 'mail' : null;", + " slags = titler.some(x => !x) ? 'chat' : null;" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r6 Return i WhatsApp Web" + }, + { + "navn": "R7-mail-return-er-send", + "fil": "mcp-server/policy.js", + "par": [ + [ + " if (tast === 'return' || tast === 'enter') return slags === 'chat' || mods.has('cmd') || mods.has('ctrl');", + " if (tast === 'return' || tast === 'enter') return true;" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r7 Return i mail" + }, + { + "navn": "B2-laan-virker-ikke", + "fil": "mcp-server/policy.js", + "par": [ + [ + " if (v === '') return !laanAktivt();", + " if (v === '') return true;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "3 ...og move tilbydes nu" + }, + { + "navn": "B2-tilbagetagning-ignoreret", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (nr !== laanNr) return;\n saetLaan(0); laanHaandtag = null;", + " if (nr !== laanNr) return;\n laanHaandtag = null;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "6 skaermen taget tilbage" + }, + { + "navn": "B2-loft-ignoreret", + "fil": "mcp-server/policy.js", + "par": [ + [ + " return v !== '' && !['0', 'false', 'no', 'off', 'nej', 'fra'].includes(v);\n}", + " return false;\n}" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "11" + }, + { + "navn": "B2-ikonets-ur-ignoreret", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " const til = Math.min(vores, Number(m.until) > 0 ? Number(m.until) : vores);", + " const til = vores;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "7 laanet udloeb" + }, + { + "navn": "R8-kun-skaermkald", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (laanAktivt()) {\n let m = null;", + " if (laanAktivt() && tagerSkaermen(name, args)) {\n let m = null;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "5c" + }, + { + "navn": "R8-egen-input", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (idle < Math.min(graense, sidenEgen)) {", + " if (idle < graense) {" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "5d" + }, + { + "navn": "B5-menneske-ignoreret", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (idle < Math.min(graense, sidenEgen)) {", + " if (false) {" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "5 mennesket bruger" + }, + { + "navn": "R13-oprindelig-fejl", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (nr !== laanNr) return;", + " if (!laanHaandtag && !laanAktivt()) return;" + ], + [ + " const t = setTimeout(() => { if (nr === laanNr && !laanAktivt()) laanHaandtag?.(); }", + " const t = setTimeout(() => { if (!laanAktivt()) { const h = laanHaandtag; laanHaandtag = null; h?.(); } }" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "7b" + }, + { + "navn": "B6-osascript-overset", + "fil": "mcp-server/index.js", + "par": [ + [ + " [/^Bash\\([^)]*osascript/i,", + " [/^INGEN-osascript/i," + ] + ], + "proeve": "test/omveje.mjs", + "forventet": "1 osascript" + }, + { + "navn": "B6-alt-naevnes", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (hvorfor) fundet.push(", + " if (true) fundet.push(" + ] + ], + "proeve": "test/omveje.mjs", + "forventet": "4 harmloese" + }, + { + "navn": "R15-skaller-overset", + "fil": "mcp-server/index.js", + "par": [ + [ + " [/^Bash\\((\\/[\\w/.-]*\\/)?(ba|z|k|c|da|fi|tc)?sh(\\s|:|\\)|$)/,", + " [/^INGEN-skal/," + ] + ], + "proeve": "test/omveje.mjs", + "forventet": "4b" + }, + { + "navn": "S2-ombryd-taber-tegn", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " linjer.append(String(ord.prefix(bredde)))\n", + "" + ] + ], + "proeve": "test/ombryd.mjs", + "forventet": "1 ord laengere" + }, + { + "navn": "Q1-ingen-binding", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (laanOejeblik() !== laanVedDom) {", + " if (false) {" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "6b" + }, + { + "navn": "Q1-stille-draebes-ikke", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (OPSLAG.has(argv[0]) || argv.includes('--dry')) continue;", + " if (OPSLAG.has(argv[0]) || argv.includes('--dry') || argv.includes('--app')) continue;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "6d" + }, + { + "navn": "Q1-oprindelig-nummerfejl", + "fil": "mcp-server/index.js", + "par": [ + [ + " let nr = null, lukketFoerStart = false;", + " let nr = ++laanNr, lukketFoerStart = false;" + ], + [ + " nr = ++laanNr; laanAfgivet = false;", + " laanAfgivet = false;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "6e ...og naar" + }, + { + "navn": "Q6-pause-efter-ikon", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " const nej = forhaandsNej(sp.text, noegle);\n if (nej) return", + " const nej = forhaandsNej(sp.text, noegle);\n if (false) return" + ] + ], + "proeve": "test/ikon-godkend.mjs", + "forventet": "G3a" + }, + { + "navn": "Q3-kolonne-ikke-krævet", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (hvem.length && sam.column !== true) {", + " if (false) {" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q3 uden kolonne" + }, + { + "navn": "Q3-soegefelt-godtaget", + "fil": "mcp-server/index.js", + "par": [ + [ + "&& felt.subrole !== 'AXSearchField';", + "&& true;" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q3 et soegefelt" + }, + { + "navn": "Q3-andet-vindue-godtaget", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (knapVindue && sam.window && knapVindue !== sam.window) {", + " if (false) {" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q3 Send-knappen ligger" + }, + { + "navn": "Q3-alle-overskrifter-som-modtager", + "fil": "mcp-server/index.js", + "par": [ + [ + " `To (read from the screen, above the text field): ${hvem[0]}`,", + " `To (read from the screen, above the text field): ${hvem.join(' - ')}`," + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q3 kun den foerste" + }, + { + "navn": "Q4-ulaeselig-titel-er-ikke-chat", + "fil": "mcp-server/index.js", + "par": [ + [ + " slags = titler.some(x => !x || WEBCHAT.test(x)) ? 'chat' : titler.some(x => WEBMAIL.test(x)) ? 'mail' : null;", + " slags = titler.some(x => x && WEBCHAT.test(x)) ? 'chat' : titler.some(x => WEBMAIL.test(x)) ? 'mail' : null;" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q4 browser med ulaeselig" + }, + { + "navn": "Q4-mail-foer-chat", + "fil": "mcp-server/index.js", + "par": [ + [ + " slags = titler.some(x => !x || WEBCHAT.test(x)) ? 'chat' : titler.some(x => WEBMAIL.test(x)) ? 'mail' : null;", + " slags = titler.some(x => WEBMAIL.test(x)) ? 'mail' : titler.some(x => !x || WEBCHAT.test(x)) ? 'chat' : null;" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q4 «Gmail - Slack»" + }, + { + "navn": "Q9-navnloes-gruppe-er-send", + "fil": "mcp-server/index.js", + "par": [ + [ + " return !rolle || /^AX(Button|Image|Unknown)$/.test(String(rolle));", + " return !rolle || /^AX(Button|Image|Unknown|Group)$/.test(String(rolle));" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q9 klik paa en navnloes gruppe" + }, + { + "navn": "Q8-sudo-overset", + "fil": "mcp-server/index.js", + "par": [ + [ + " [/^Bash\\((\\/[\\w/.-]*\\/)?(sudo|doas)(\\s*\\*|:\\*|\\s+(\\/\\S*\\/)?((ba|z|k|c|da|fi|tc)?sh|python\\d*|node|ruby|perl|osascript|open)\\b)/,", + " [/^INGEN-sudo/," + ] + ], + "proeve": "test/omveje.mjs", + "forventet": "4d" + }, + { + "navn": "R3-kontrol-uden-vindue", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!knapVindue || !sam.window || !sammeRaekke) {", + " if (!sammeRaekke) {" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q3 Send-knappens vindue kan ikke" + }, + { + "navn": "R3-kontrol-ikke-ved-feltet", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!knapVindue || !sam.window || !sammeRaekke) {", + " if (!knapVindue || !sam.window) {" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q3 Send-knappen ligger ikke ved" + }, + { + "navn": "R3-ukendt-rolle-ufarlig", + "fil": "mcp-server/index.js", + "par": [ + [ + " return !rolle || /^AX(Button|Image|Unknown)$/.test(String(rolle));", + " return /^AX(Button|Image)$/.test(String(rolle));" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "q9 klik paa et element med ukendt" + }, + { + "navn": "R4-ax-flytter-stempel", + "fil": "mcp-server/index.js", + "par": [ + [ + "POSTER_INPUT.has(name) && res.__postedeInput !== false) sidsteEgenHandling", + "POSTER_INPUT.has(name)) sidsteEgenHandling" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "5e" + }, + { + "navn": "R4-kun-lodret-raekke", + "fil": "mcp-server/index.js", + "par": [ + [ + "\n && k.x >= f.x - 16 && k.x <= f.x + f.w + 160;", + ";" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r4a" + }, + { + "navn": "R4-opslagsfejl-springer-binding", + "fil": "mcp-server/index.js", + "par": [ + [ + " erKontrol = true;\n const k = await kontrol();", + " const k = await kontrol();\n erKontrol = !!(k && !k.fejl);" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "11b" + }, + { + "navn": "R4-browser-kun-fokus-titel", + "fil": "mcp-server/index.js", + "par": [ + [ + " const titler = [String(t.window || '').trim(), ...(k ? [k.fejl ? '' : String(k.vindue || '').trim()] : [])];", + " const titler = [String(t.window || '').trim()];" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r4c" + }, + { + "navn": "R4-paste-undtaget", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (OPSLAG.has(argv[0]) || argv.includes('--dry')) continue;", + " if (OPSLAG.has(argv[0]) || argv.includes('--dry') || argv[0] === 'paste') continue;" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "6f" + }, + { + "navn": "R5-nul-binding-skjuler-laan", + "fil": "mcp-server/index.js", + "par": [ + [ + " const laanVedDom = laanOejeblik();", + " const laanVedDom = laanAktivt() ? laanNr : 0;" + ], + [ + " if (laanOejeblik() !== laanVedDom) {", + " if ((laanAktivt() ? laanNr : 0) !== laanVedDom) {" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "6g" + }, + { + "navn": "R5-ulaeseligt-kontrolvindue-ignoreres", + "fil": "mcp-server/index.js", + "par": [ + [ + " const titler = [String(t.window || '').trim(), ...(k ? [k.fejl ? '' : String(k.vindue || '').trim()] : [])];", + " const titler = [String(t.window || '').trim(), ...(k?.fejl ? [''] : k?.vindue ? [String(k.vindue).trim()] : [])];" + ] + ], + "proeve": "test/sende-port.mjs", + "forventet": "r4d" + }, + { + "navn": "Q10-praecise-kommandoer-taeller", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (arg !== null && r !== 'Bash' && !/\\*/.test(arg) && !/(^|\\s)-$/.test(arg.trim())) continue;", + " if (false) continue;" + ] + ], + "proeve": "test/omveje.mjs", + "forventet": "4f" + }, + { + "navn": "Q11-menu-genvej-overset", + "fil": "mcp-server/index.js", + "proeve": "test/menu-genvej.mjs", + "forventet": "2 et punkt med Cmd+Q", + "par": [ + [ + " return !!p.shortcut && tastSerFarlig(p.shortcut);", + " return false;" + ] + ] + }, + { + "navn": "Q12-menu-ukendt-aabner", + "fil": "mcp-server/index.js", + "proeve": "test/menu-genvej.mjs", + "forventet": "3 et punkt der ikke kan", + "par": [ + [ + " if (!p) return true;", + " if (!p) return false;" + ] + ] + }, + { + "navn": "Q13-lukke-ord-vaek", + "fil": "mcp-server/policy.js", + "proeve": "test/claims.mjs", + "forventet": "11. farlige menustier", + "par": [ + [ + " return FARLIGE_MENUORD.some(o => p.includes(o)) || LUKKE_ORD.test(p);", + " return FARLIGE_MENUORD.some(o => p.includes(o));" + ] + ] + }, + { + "navn": "Q14-tilbagerykning-overset", + "fil": "test/forrest-log.mjs", + "proeve": "test/forrest-log-kalibrering.mjs", + "forventet": "10 rød", + "par": [ + [ + " if (s.aarsag === 'SetFrontProcess' && forrige", + " if (false && s.aarsag === 'SetFrontProcess' && forrige" + ] + ] + }, + { + "navn": "Q15-agent-forrest-overset", + "fil": "test/forrest-log.mjs", + "proeve": "test/forrest-log-kalibrering.mjs", + "forventet": "5 rød", + "par": [ + [ + " if (agentPid(s.pid) && s.aarsag !== 'SetManagedDisplayCurrentSpace')", + " if (false)" + ] + ] + }, + { + "navn": "Q16-mennesket-er-agent", + "fil": "test/forrest-log.mjs", + "proeve": "test/forrest-log-kalibrering.mjs", + "forventet": "15 grøn", + "par": [ + [ + " if (agentPid(s.pid) && s.aarsag !== 'SetManagedDisplayCurrentSpace')", + " if (s.aarsag === 'SetManagedDisplayCurrentSpace')" + ] + ] + }, + { + "navn": "Q17-launch-slaar-ikke-op", + "fil": "mcp-server/index.js", + "proeve": "test/launch-lukket.mjs", + "forventet": "1 et lukket", + "par": [ + [ + " targetBundleId = r.bundleId || null;", + " targetBundleId = null;" + ] + ] + }, + { + "navn": "Q18-doer-sti-smugles", + "fil": "mcp-server/index.js", + "proeve": "test/doer.mjs", + "forventet": "4 et forsoeg", + "par": [ + [ + " if (!/^[0-9A-Za-z]{22}$/.test(id)) return errorResult(", + " if (false) return errorResult(" + ] + ] + }, + { + "navn": "Q19-laering-nummer-slipper", + "fil": "mcp-server/index.js", + "proeve": "test/laering.mjs", + "forventet": "2b et telefonnummer", + "par": [ + [ + " .replace(/\\+?\\d[\\d ,.\\-]{4,}\\d/g, '[number]')\n", + "\n" + ] + ] + }, + { + "navn": "Q20-quit-spoerger-ikke", + "fil": "mcp-server/index.js", + "proeve": "test/claims-offline.mjs", + "forventet": "quit spoerger", + "par": [ + [ + " || name === 'computer_quit'\n", + "\n" + ] + ] + }, + { + "navn": "Q21-luk-vindue-spoerger-ikke", + "fil": "mcp-server/index.js", + "proeve": "test/claims-offline.mjs", + "forventet": "at lukke et vindue", + "par": [ + [ + " || (name === 'computer_window' && args.button === 'close')\n", + "\n" + ] + ] + }, + { + "navn": "Q22-optagelse-spoerger-ikke", + "fil": "mcp-server/index.js", + "proeve": "test/optagelse.mjs", + "forventet": "2 start spoerger", + "par": [ + [ + " || optagStart\n", + "\n" + ] + ] + }, + { + "navn": "Q23-kaede-brud-overset", + "fil": "mcp-server/audit.js", + "proeve": "test/audit-chain.mjs", + "forventet": "en AENDRET linje", + "par": [ + [ + " ok: aegte.length === 0 && !haleFjernet, checked: tjekket,", + " ok: true, checked: tjekket," + ] + ] + }, + { + "navn": "Q24-modtager-overses", + "fil": "mcp-server/index.js", + "proeve": "test/sende-port.mjs", + "forventet": "w1 modtageren", + "par": [ + [ + " const hvem = [...(sam.recipient ? [sam.recipient] : (sam.headings || []))]", + " const hvem = [...(sam.headings || [])]" + ] + ] + }, + { + "navn": "Q25-slet-knap-spoerger-ikke", + "fil": "mcp-server/index.js", + "proeve": "test/knap-ord.mjs", + "forventet": "1 en knap der hedder", + "par": [ + [ + " alwaysAsk: menuFarlig || knapFarlig", + " alwaysAsk: menuFarlig" + ] + ] + }, + { + "navn": "Q26-klik-fejler-aabent", + "fil": "mcp-server/index.js", + "proeve": "test/bundet-maal.mjs", + "forventet": "11a", + "par": [ + [ + " if (!d?.found) return true;", + " if (!d?.found) return false;" + ] + ] + }, + { + "navn": "Q27-tryk-fejler-aabent", + "fil": "mcp-server/index.js", + "proeve": "test/knap-ord.mjs", + "forventet": "11 dry-run finder intet", + "par": [ + [ + " if (!el) return { afvis: 'Refused: what would be pressed could not be determined. Nothing was pressed, and nobody was asked.', grund: 'press: target unknown' };", + " if (!el) return { farlig: false };" + ] + ] + }, + { + "navn": "Q28-toerkoersel-fejler-aabent", + "fil": "mcp-server/index.js", + "proeve": "test/knap-ord.mjs", + "forventet": "12 selve toerkoerslen fejler", + "par": [ + [ + " return { afvis: `Refused: what would be pressed could not be determined (${err?.message || 'unknown error'}). Nothing was pressed, and nobody was asked.`, grund: 'press: lookup failed' };", + " return { farlig: false };" + ] + ] + }, + { + "navn": "Q29-umuligt-tryk-spoerger-igen", + "fil": "mcp-server/index.js", + "proeve": "test/knap-ord.mjs", + "forventet": "13 not-found", + "par": [ + [ + " if (err instanceof HelperError && (err.code === 'not-found' || err.code === 'ambiguous')) {", + " if (false && err instanceof HelperError && (err.code === 'not-found' || err.code === 'ambiguous')) {" + ] + ] + }, + { + "navn": "Q30-ukendt-ejer-spoerger-igen", + "fil": "mcp-server/index.js", + "proeve": "test/knap-ord.mjs", + "forventet": "15 klik uden app", + "par": [ + [ + " if (ejerUkendt && currentMode() !== 'readonly') {", + " if (false && ejerUkendt && currentMode() !== 'readonly') {" + ] + ] + }, + { + "navn": "Q31-sende-port-foer-readonly", + "fil": "mcp-server/index.js", + "proeve": "test/readonly-foerst.mjs", + "forventet": "computer_press i readonly", + "par": [ + [ + " const sende = (effektivTier === TIER.READ || kunLaes) ? null : await sendeDom(name, args, targetBundleId, appKlik);", + " const sende = effektivTier === TIER.READ ? null : await sendeDom(name, args, targetBundleId, appKlik);" + ] + ] + }, + { + "navn": "P1-maaling-fjernet-fra-loglinjen", + "fil": "mcp-server/index.js", + "proeve": "test/tilstede.mjs", + "forventet": "1 ja: maalingen staar", + "par": [ + [ + " ...(verdict.asker === 'menubar' ? { asker: 'menubar', presence: tagMaaling() } : {}),\n decision: verdict.allow", + " ...(verdict.asker === 'menubar' ? { asker: 'menubar' } : {}),\n decision: verdict.allow" + ] + ] + }, + { + "navn": "P2-slut-idle-springes-over", + "fil": "mcp-server/tilstede.js", + "proeve": "test/tilstede.mjs", + "forventet": "2 udloeb", + "par": [ + [ + " const slut = besvaretJa ? 'answered' : await loft(idleNu(), 1500, 'unreadable');", + " const slut = 'answered';" + ] + ] + }, + { + "navn": "P3-ulaeselig-bliver-nul", + "fil": "mcp-server/tilstede.js", + "proeve": "test/tilstede.mjs", + "forventet": "3 ulaeselig", + "par": [ + [ + " return Number.isFinite(v) && v >= 0 ? Math.round(v * 10) / 10 : 'unreadable';", + " return Number.isFinite(v) && v >= 0 ? Math.round(v * 10) / 10 : 0;" + ] + ] + }, + { + "navn": "P4-laanet-uden-maaling", + "fil": "mcp-server/index.js", + "proeve": "test/tilstede.mjs", + "forventet": "6 skaermlaan", + "par": [ + [ + "decision, asked, ...(asked ? { asker: 'menubar', presence: tagMaaling() } : {}), reason });", + "decision, asked, ...(asked ? { asker: 'menubar' } : {}), reason });" + ] + ] + }, + { + "navn": "M1-modul-mangler-i-pakken", + "fil": "mcp-server/package.json", + "proeve": "test/pakke-filer.mjs", + "forventet": "hver fil index.js importerer", + "par": [ + [ + " \"tilstede.js\",\n", + "" + ] + ] + }, + { + "navn": "D1-venter-ikke-paa-pause", + "fil": "mcp-server/index.js", + "proeve": "test/skaerm-laan.mjs", + "forventet": "D1 mennesket holder pause", + "par": [ + [ + " if (laanAktivt()) await ventPaaStilhed(laanVedDom);\n", + "" + ] + ] + }, + { + "navn": "D3-ventetiden-uden-loft", + "fil": "mcp-server/index.js", + "proeve": "test/skaerm-laan.mjs", + "forventet": "D3 mennesket bliver ved", + "par": [ + [ + " const slut = Date.now() + Math.min(ventSek() * 1000, restMs());", + " const slut = Date.now() + 3 * ventSek() * 1000;" + ] + ] + }, + { + "navn": "E1-gh-fejl-bliver-ikke-koert", + "fil": "scripts/release.sh", + "proeve": "test/release-fejlmatrix.mjs", + "forventet": "S1 gh fejler", + "par": [ + [ + "if ! CIDOM=$(gh run list", + "if false && ! CIDOM=$(gh run list" + ] + ] + }, + { + "navn": "E2-lsremote-fejl-bliver-ledigt", + "fil": "scripts/release.sh", + "proeve": "test/release-fejlmatrix.mjs", + "forventet": "S2 ls-remote fejler", + "par": [ + [ + " *) echo \"⛔ GitHub kunne ikke spoerges om maerket", + " *) FJERN_TAG=\"\" ;;\n zz) echo \"⛔ GitHub kunne ikke spoerges om maerket" + ] + ] + }, + { + "navn": "E3-faelden-armeret-gennem-deprecate", + "fil": "scripts/release.sh", + "proeve": "test/release-fejlmatrix.mjs", + "forventet": "S3 publish lykkedes", + "par": [ + [ + "( cd mcp-server && npm publish --access public )\n# ⛔ 7/10 (panel R2+R8, E): faelden stod armeret gennem deprecate.", + "( cd mcp-server && npm publish --access public )\n# (mutant: ingen afvaebning her)" + ], + [ + "trap - EXIT\n# ⛔ 25/9 (fyld-tjek)", + "# ⛔ 25/9 (fyld-tjek)" + ] + ] + }, + { + "navn": "E4-reservelinjen-kan-ikke-koeres", + "fil": "scripts/release.sh", + "proeve": "test/release-fejlmatrix.mjs", + "forventet": "S4 deprecate fejler", + "par": [ + [ + " printf \" npm deprecate '%s' '%s'\\n\" \"@agent360/computer-mcp@$SAARBAR\" \"$DEPR_BESKED\"; }", + " echo \" npm deprecate @agent360/computer-mcp@$SAARBAR \\\"...\\\"\"; }" + ] + ] + }, + { + "navn": "E5-login-fejl-fortsaetter", + "fil": "tools/faerdiggoer-udgivelsen.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F2 login fejler", + "par": [ + [ + " $PUB login github || { echo \"⛔ login til registret fejlede - intet er sendt til registret. Koer scriptet igen.\"; exit 1; }", + " $PUB login github || echo \"login fejlede\"" + ] + ] + }, + { + "navn": "E6-tom-liste-er-findes", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F8 tom PR-liste", + "par": [ + [ + ".[0] | select(. != null) | \"\\(.number) \\(.state)\"", + ".[0] | \"\\(.number) \\(.state)\"" + ] + ] + }, + { + "navn": "E7-synkronisering-springes-over", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F6 halv synkronisering", + "par": [ + [ + " # en halv rettelse, som næste kørsel committede (kun PUBLICERET, forbeholdet stadig på siden).\n goer python3 scripts/sync-tal.py\n", + " # en halv rettelse, som næste kørsel committede (kun PUBLICERET, forbeholdet stadig på siden).\n" + ] + ] + }, + { + "navn": "E8-npm-readme-tabes", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F3 ...grenen hedder", + "par": [ + [ + "FILER=\"PUBLICERET README.md docs mcp-server/README.md server.json ", + "FILER=\"PUBLICERET README.md docs server.json " + ] + ] + }, + { + "navn": "K1-plugin-peger-paa-latest", + "fil": "plugin/.claude-plugin/plugin.json", + "proeve": "test/plugin-manifest.mjs", + "forventet": "serveren startes med npx", + "par": [ + [ + "\"-y\", \"@agent360/computer-mcp@", + "\"-y\", \"@agent360/computer-mcp@latest-" + ] + ] + }, + { + "navn": "F1-proeve-uden-egen-tilstand", + "fil": "test/knap-ord.mjs", + "proeve": "test/tilstand-isoleret.mjs", + "forventet": "hver serverstartende proeve", + "par": [ + [ + "import './egen-tilstand.mjs';\n", + "" + ] + ] + }, + { + "navn": "F2-proeve-kan-ramme-menneskets-ikon", + "fil": "test/egen-tilstand.mjs", + "proeve": "test/tilstand-isoleret.mjs", + "forventet": "egen-tilstand.mjs slaar ogsaa", + "par": [ + [ + "if (process.env.CMCP_STATUS_IKON === undefined) process.env.CMCP_STATUS_IKON = '0';", + "" + ] + ] + }, + { + "navn": "H1-alle-tryk-farlige", + "fil": "mcp-server/index.js", + "proeve": "test/claims.mjs", + "forventet": "5b. harmloest program", + "par": [ + [ + "name === 'computer_press' ? !!trykDom?.farlig", + "name === 'computer_press' ? true" + ] + ] + }, + { + "navn": "H2-terminal-spoerger-ikke", + "fil": "mcp-server/policy.js", + "proeve": "test/claims.mjs", + "forventet": "5. press bedoemmes", + "par": [ + [ + " const nytSessionsProgram = targetBundleId\n && SPOERG_PR_SESSION.has(targetBundleId)", + " const nytSessionsProgram = targetBundleId\n && false && SPOERG_PR_SESSION.has(targetBundleId)" + ] + ] + }, + { + "navn": "I1-ukendt-ejer-klikker-efter-ja", + "fil": "mcp-server/index.js", + "proeve": "test/klik-ejer.mjs", + "forventet": "9a ukendt ejer ved vurderingen", + "par": [ + [ + " if (ejerUkendt && currentMode() !== 'readonly') {", + " if (false && ejerUkendt && currentMode() !== 'readonly') {" + ] + ] + }, + { + "navn": "K33-tryk-naar-ikke-koeen", + "fil": "mcp-server/index.js", + "proeve": "test/claims.mjs", + "forventet": "33. de tre afviste handlinger", + "par": [ + [ + " if (verdict.koe) {\n noterVentende(", + " if (verdict.koe && name !== 'computer_press') {\n noterVentende(" + ] + ] + }, + { + "navn": "H3-attrap-sender-opslag-videre", + "fil": "test/falsk-hjaelper.mjs", + "proeve": "test/attrap-faste-opslag.mjs", + "forventet": "standard: apps svarer fast", + "par": [ + [ + "const FASTE = ${aegteOpslag ? 'null' : JSON.stringify(FASTE_OPSLAG)};", + "const FASTE = null;" + ] + ] + }, + { + "navn": "H4-aegte-opslag-ignoreres", + "fil": "test/falsk-hjaelper.mjs", + "proeve": "test/attrap-faste-opslag.mjs", + "forventet": "aegteOpslag: apps kommer", + "par": [ + [ + "const FASTE = ${aegteOpslag ? 'null' : JSON.stringify(FASTE_OPSLAG)};", + "const FASTE = ${false ? 'null' : JSON.stringify(FASTE_OPSLAG)};" + ] + ] + }, + { + "navn": "L1-readme-uddrag-redigeret", + "fil": "README.md", + "proeve": "test/readme-log.mjs", + "forventet": "hver linje i uddraget staar byte for byte", + "par": [ + [ + "\"decision\":\"denied\",\"asked\":true,", + "\"decision\":\"allowed\",\"asked\":true," + ] + ] + }, + { + "navn": "L2-loggen-redigeret", + "fil": "test/fixtures/audit-readme.jsonl", + "proeve": "test/readme-log.mjs", + "forventet": "loggens kaede holder", + "par": [ + [ + "\"reason\":\"read-only action\"", + "\"reason\":\"read-only action.\"" + ] + ] + }, + { + "navn": "S1-lyd-ved-genlevering", + "fil": "helper/Sources/cmcp-status/Lyd.swift", + "proeve": "test/ikon-lyd.mjs", + "forventet": "2 samme spoergsmaal leveret igen", + "par": [ + [ + "guard til, !lukket, !besvaret, !hoerte.contains(nonce) else { return false }", + "guard til, !lukket, !besvaret else { return false }" + ] + ] + }, + { + "navn": "S2-lyd-ved-udloeb", + "fil": "helper/Sources/cmcp-status/Lyd.swift", + "proeve": "test/ikon-lyd.mjs", + "forventet": "3 udloebet", + "par": [ + [ + "guard til, !lukket, !besvaret, !hoerte.contains(nonce) else { return false }", + "guard til, !besvaret, !hoerte.contains(nonce) else { return false }" + ] + ] + }, + { + "navn": "S3-lyd-fra-mod-gustavs-valg", + "fil": "helper/Sources/cmcp-status/Lyd.swift", + "proeve": "test/ikon-lyd.mjs", + "forventet": "9 standarden er til", + "par": [ + [ + "let LYD_STANDARD = true", + "let LYD_STANDARD = false" + ] + ] + }, + { + "navn": "S4-lyd-uden-om-reglen", + "fil": "helper/Sources/cmcp-status/main.swift", + "proeve": "test/ikon-lyd.mjs", + "forventet": "10 ikonet har én lyd", + "par": [ + [ + "if lydHukommelse.skalLyde(", + "if true || lydHukommelse.skalLyde(" + ] + ] + }, + { + "navn": "P5-ask-user-uden-maaling", + "fil": "mcp-server/index.js", + "proeve": "test/tilstede.mjs", + "forventet": "3b ask_user", + "par": [ + [ + "if (maaling) Object.defineProperty(res, '__presence'", + "if (false && maaling) Object.defineProperty(res, '__presence'" + ] + ] + }, + { + "navn": "P6-ny-version-om-gammel-proces", + "fil": "mcp-server/tilstede.js", + "proeve": "test/tilstede.mjs", + "forventet": "4 versionsfilen skiftet", + "par": [ + [ + "icon = !v ? 'unknown' : skiftet ? 'replaced since start' : v;", + "icon = !v ? 'unknown' : v;" + ] + ] + }, + { + "navn": "P7-ikonets-pid-ignoreres", + "fil": "mcp-server/tilstede.js", + "proeve": "test/tilstede.mjs", + "forventet": "1 ja: ikonets version er DETTE", + "par": [ + [ + "try { pid = readFileSync(IKON_PID_FIL, 'utf8').trim(); } catch { pid = null; }", + "pid = null;" + ] + ] + }, + { + "navn": "D4-sidste-opslag-faar-hele-fristen", + "fil": "mcp-server/index.js", + "proeve": "test/skaerm-laan.mjs", + "forventet": "D4 langsomme opslag", + "par": [ + [ + "try { m = await callHelper(['idle'], { timeout: Math.min(2000, tilbage) }); }", + "try { m = await callHelper(['idle'], { timeout: 2000 }); }" + ] + ] + }, + { + "navn": "E9-genoptag-uden-commit", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F9 genoptaget", + "par": [ + [ + " if [ \"$(git branch --show-current)\" = \"$GREN\" ] && [ \"$(git rev-parse \"$GREN\")\" = \"$(git rev-parse origin/main)\" ]", + " if false && [ \"$(git rev-parse \"$GREN\")\" = \"$(git rev-parse origin/main)\" ]" + ] + ] + }, + { + "navn": "H5-dry-sluges-som-handling", + "fil": "test/falsk-hjaelper.mjs", + "proeve": "test/attrap-faste-opslag.mjs", + "forventet": "aegteOpslag: press --dry", + "par": [ + [ + "if (!OPSLAG.has(kommando) && !(kommando === 'press' && argv.includes('--dry'))) {", + "if (!OPSLAG.has(kommando)) {" + ] + ] + }, + { + "navn": "S5-nyt-spoergsmaal-naar-ikke-lyden", + "fil": "helper/Sources/cmcp-status/main.swift", + "proeve": "test/ikon-lyd.mjs", + "forventet": "13 et nyt spoergsmaal", + "par": [ + [ + "nyAnmodning = { a in ikon.vis(a) }", + "nyAnmodning = { _ in ikon.tik() }" + ] + ] + }, + { + "navn": "Q32-tidsudloeb-i-toerkoersel-er-ufarligt", + "fil": "mcp-server/index.js", + "proeve": "test/knap-ord.mjs", + "forventet": "16 toerkoerslen naar ikke frem", + "par": [ + [ + " return { afvis: 'Refused: finding the element took too long, so what would be pressed could not be confirmed. Nothing was pressed, and nobody was asked. Try again.', grund: 'press: lookup timed out' };", + " return { farlig: false };" + ] + ] + }, + { + "navn": "L3-readme-presence-usand", + "fil": "README.md", + "proeve": "test/readme-log.mjs", + "forventet": "README siger hvor hvert presence-felt", + "par": [ + [ + "Only the box setting was read from", + "Every field was read from" + ] + ] + }, + { + "navn": "E10-genoptagelse-tager-fremmed-staged-med", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F10", + "par": [ + [ + "\n goer git commit -m \"release: $V er udgivet - forbeholdene væk\" -- $FILER\n", + "\n goer git commit -m \"release: $V er udgivet - forbeholdene væk\"\n" + ] + ] + }, + { + "navn": "E11-ny-gren-tager-fremmed-staged-med", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F11", + "par": [ + [ + "\n goer git commit -m \"release: $V er udgivet - forbeholdene væk\" -- $FILER\n", + "\n goer git commit -m \"release: $V er udgivet - forbeholdene væk\"\n" + ] + ] + }, + { + "navn": "B5a-sync-tal-roerer-ikke-pluginnet", + "fil": "scripts/sync-tal.py", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F3 ...dok-PR'en loefter pluginnet", + "par": [ + [ + "if re.fullmatch(r'\\d+\\.\\d+\\.\\d+', UDGIVET):\n for _sti, _moenstre", + "if False:\n for _sti, _moenstre" + ] + ] + }, + { + "navn": "B5b-dok-pr-uden-pluginnet", + "fil": "tools/dok-pr.sh", + "proeve": "test/efter-udgivelsen.mjs", + "forventet": "F3 ...dok-PR'en loefter pluginnet", + "par": [ + [ + " server.json plugin/.claude-plugin/plugin.json .claude-plugin/marketplace.json\"", + " server.json\"" + ] + ] + }, + { + "navn": "A1-advarsel-rammer-0.2.1", + "fil": "scripts/release.sh", + "proeve": "test/release-fejlmatrix.mjs", + "forventet": "S6 advarslen rammer kun versioner foer 0.2.1", + "par": [ + [ + "SAARBAR=\"<0.2.1\"", + "SAARBAR=\"<$V\"" + ] + ] + }, + { + "navn": "B1-forbehold-ved-samme-vaerktoejstal", + "fil": "scripts/sync-tal.py", + "proeve": "test/sync-tal-versioner.mjs", + "forventet": "samme vaerktoejstal som kilden", + "par": [ + [ + "VAERKTOEJS_AFSTAND = AFSTAND and (K is None or K != N)", + "VAERKTOEJS_AFSTAND = AFSTAND" + ] + ] + }, + { + "navn": "C4-plugin-vejen-forsvinder-fra-README", + "fil": "README.md", + "proeve": "test/plugin-manifest.mjs", + "forventet": "README.md viser plugin-vejen", + "par": [ + [ + "/plugin install computer-mcp@agent360", + "/plugin install computer-mcp" + ] + ] + }, + { + "navn": "U1-usynlige-tegn-taeller-med-i-serveren", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const USYNLIGE = /\\p{Cf}/gu;", + "const USYNLIGE = /(?!)/gu;" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1.1 serveren" + }, + { + "navn": "U2-serveren-vaelger-ved-tvivl", + "fil": "mcp-server/helper.js", + "par": [ + [ + "return hits.length === 1 && !deltId(hits[0]) ? hits[0] : null;", + "return hits[0] && !deltId(hits[0]) ? hits[0] : null;" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1.2 serveren" + }, + { + "navn": "U3-usynlige-tegn-taeller-med-i-hjaelperen", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "$0.properties.generalCategory != .format", + "_ = $0; return true" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2.1 hjaelperen" + }, + { + "navn": "U4-hjaelperen-vaelger-ved-tvivl", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "return hits.count == 1 ? .fundet(hits[0]) : (hits.isEmpty ? .intet : .tvetydig)", + "return hits.isEmpty ? .intet : .fundet(hits[0])" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2.2 hjaelperen" + }, + { + "navn": "U5-koerende-program-uden-reglen", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + "static func app(bundleId: String) -> NSRunningApplication? { appOpslag(bundleId).app }", + "static func app(bundleId: String) -> NSRunningApplication? { allApps().first { $0.bundleIdentifier == bundleId } ?? appOpslag(bundleId).app }" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6a AX.app" + }, + { + "navn": "U6-lukket-program-forkert-valg", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + "else { return nil }\n return fund[i]", + "else { return nil }\n return fund.first" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6b" + }, + { + "navn": "U7-porten-uden-reglen", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const hit = findProgram(r.apps || [], want);\n return hit ? (hit.bundleId || null) : null;", + "const hit = (r.apps || []).find(a => [a.bundleId, a.name].some(v => (v || '').toLowerCase() === want.toLowerCase()));\n return hit ? (hit.bundleId || null) : null;" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "4 resolveBundleId" + }, + { + "navn": "U8-forrest-opslaget-uden-reglen", + "fil": "mcp-server/helper.js", + "par": [ + [ + " const hit = findProgram(r.apps, want);\n if (!hit) return null;", + " const hit = r.apps.find(a => [a.bundleId, a.name].some(v => (v || '').toLowerCase() === want.toLowerCase()));\n if (!hit) return null;" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "5 resolveApp" + }, + { + "navn": "U9-hjaelperen-oversaetter-ikke-app", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + "let args = Args(oversatArgv.argv)", + "let args = Args(CommandLine.arguments)" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6c" + }, + { + "navn": "U10-trim-tager-NEL", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "[0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x20, 0xA0, 0x1680]", + "[0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x20, 0x85, 0xA0, 0x1680]" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "3a trim" + }, + { + "navn": "U11-trim-glemmer-BOM", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "0x205F, 0x3000, 0xFEFF])", + "0x205F, 0x3000])" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "3a trim" + }, + { + "navn": "U12-serveren-smaa-paa-hele-strengen", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const smaa = (s) => Array.from(String(s ?? ''), c => c.toLowerCase()).join('');", + "const smaa = (s) => String(s ?? '').toLowerCase();" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1.15 serveren" + }, + { + "navn": "U13-hjaelperen-kanonisk-lighed", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "navne.firstIndex(where: { smaa(Array($0.unicodeScalars)) == lav })", + "navne.firstIndex(where: { String(String.UnicodeScalarView(smaa(Array($0.unicodeScalars)))) == String(String.UnicodeScalarView(lav)) })" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2.16 hjaelperen" + }, + { + "navn": "U14-serveren-id-uden-praecist-trin", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const viaId = apps.find(a => (a.bundleId || '') === w)\n || ", + "const viaId = " + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1a.2 serveren" + }, + { + "navn": "U15-hjaelperen-id-uden-store-smaa-trin", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + " if let i = ids.firstIndex(where: { $0.map { smaa(Array($0.unicodeScalars)) } == lw }) { return .fundet(i) }\n", + " _ = lw\n" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2a.1 hjaelperen" + }, + { + "navn": "U16-serveren-normaliserer-foer-praecist", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const w = String(want ?? '').trim();", + "const w = navneNoegle(want);" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1.5 serveren" + }, + { + "navn": "U17-hjaelperen-normaliserer-foer-praecist", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "let lav = smaa(trim(want))", + "let lav = noegle(want)" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2.5 hjaelperen" + }, + { + "navn": "U18-oversaettelsen-doed", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "where a[i] == \"--app\" && !a[i + 1].hasPrefix(\"--\")", + "where a[i] == \"--app-x\" && !a[i + 1].hasPrefix(\"--\")" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2c.1" + }, + { + "navn": "U19-leveringen-sammenligner-navne-selv", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + "guard let app = AX.app(bundleId: navn) else {", + "guard let app = AX.app(bundleId: navn) ?? AX.allApps().first(where: { $0.localizedName?.lowercased() == navn.lowercased() }) else {" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6d" + }, + { + "navn": "U20-vinduesfilter-sammenligner-navne-selv", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + "if let scope = args.str(\"app\"), !AX.passer(a, scope) { continue }", + "if let scope = args.str(\"app\"), !AX.passer(a, scope), a.localizedName?.lowercased() != scope.lowercased() { continue }" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6d" + }, + { + "navn": "U21-opstart-tvetydig-falder-til-disken", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if o.tvetydig || kunPraecistId { return (nil, false) }\n", + "" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6e" + }, + { + "navn": "U22-serveren-trimmer-ikke", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const w = String(want ?? '').trim();\n if (!w) return null;", + "const w = String(want ?? '');\n if (!w) return null;" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1.14 serveren" + }, + { + "navn": "X1-boksens-allow-uden-touch-id", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "case .tillad(let n): tilladNonce(n, kilde: \"allow-box\")", + "case .tillad(let n): aaben(n)?.svar(ok: true)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5a" + }, + { + "navn": "X2-boksens-allow-ved-lang-tekst", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if kilde == \"allow-box\" && renTekst(a.s.text).count > BOKS_MAX_TEGN { return }\n", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5c" + }, + { + "navn": "X3-allow-i-boksen-ved-lang-tekst", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + ".filter { helVist || $0 != \"Allow… (confirm with Touch ID)\" }", + ".filter { _ in true }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "2c" + }, + { + "navn": "X4-placering-efter-musen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "let i = boksSkaerm(forrestVindue: forrestVindue(), mus: NSEvent.mouseLocation, skaerme: skaerme.map { $0.frame })\n let f = skaerme[i].visibleFrame", + "let i = boksSkaerm(forrestVindue: nil, mus: NSEvent.mouseLocation, skaerme: skaerme.map { $0.frame })\n let f = skaerme[i].visibleFrame" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5g" + }, + { + "navn": "X5-skaermvalget-ser-bort-fra-vinduet", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " if let v = forrestVindue, let i = skaerme.firstIndex(where: { $0.contains(CGPoint(x: v.midX, y: v.midY)) }) { return i }\n", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "3a" + }, + { + "navn": "X6-vinduets-ramme-ikke-omregnet", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "CGRect(x: cg.minX, y: hovedHoejde - cg.maxY, width: cg.width, height: cg.height)", + "CGRect(x: cg.minX, y: cg.minY, width: cg.width, height: cg.height)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "3c" + }, + { + "navn": "X7-boksen-i-skaermbilleder", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " p.sharingType = .none\n", + " p.sharingType = .readOnly\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5i" + }, + { + "navn": "X8-ny-skaerm-flytter-ikke-boksen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if self?.boks.isVisible == true { self?.boks.placer() }", + " _ = self" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5h" + }, + { + "navn": "X9-skifte-skaerm-flytter-ikke-boksen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "\n else if boks.skalFlyttes() { boks.placer() }", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5h" + }, + { + "navn": "X10-knappen-ikke-bundet-til-spoergsmaalet", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "vist = ind; nonce = ny", + "vist = ind" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5e" + }, + { + "navn": "X11-menuens-knaptekst-i-boksen", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "menuKnap == \"Allow… (confirm with Touch ID)\" ? \"Allow (Touch ID)\" : menuKnap", + "menuKnap" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "2b" + }, + { + "navn": "X12-boksen-paastaar-hele-teksten", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "let helVist = hel.count <= BOKS_MAX_TEGN", + "let helVist = true" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "2c" + }, + { + "navn": "X13-agentens-navn-uvasket", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "\"\\(renTekst(v.klient ?? \"An agent\")) needs you\"", + "\"\\(v.klient ?? \"An agent\") needs you\"" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "2g" + }, + { + "navn": "Y1-tidlig-retur-foer-reglen", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + "false) }\n switch Navne.vaelgApp(", + "false) }\n if let a = alle.first(where: { Navne.noegle($0.localizedName ?? \"\") == Navne.noegle(hvad) }) { return (a, false) }\n switch Navne.vaelgApp(" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6a2" + }, + { + "navn": "Y2-foerste-program-paa-disken", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " var fund: [URL] = []\n", + " if let f = (try? FileManager.default.contentsOfDirectory(atPath: mapper[0]))?.first(where: { $0.hasSuffix(\".app\") }) { return URL(fileURLWithPath: mapper[0]).appendingPathComponent(f) }\n var fund: [URL] = []\n" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6b" + }, + { + "navn": "Y3-nej-bliver-til-ja", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "case .svar(let v):\n a.svar(ok: v)", + "case .svar(let v):\n _ = v; a.svar(ok: true)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5c2" + }, + { + "navn": "Y4-spoergsmaal-uden-engangsnummer", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "return anmodninger.first { $0.s.nonce == n && !$0.besvaret }", + "return anmodninger.first { _ = n; return !$0.besvaret }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5c3" + }, + { + "navn": "Y5-boksen-uden-for-skaermen", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "CGPoint(x: synlig.maxX - bredde - 16, y: synlig.maxY - 12)", + "CGPoint(x: synlig.minX - bredde - 16, y: synlig.maxY - 12)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "3d" + }, + { + "navn": "Y6-et-andet-programs-vindue", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "(w[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value == pid", + "(w[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value != pid" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "3e" + }, + { + "navn": "Y7-menulinje-lag-som-vindue", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "\n && (w[kCGWindowLayer as String] as? NSNumber)?.intValue == 0", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "3e" + }, + { + "navn": "Y9-placering-uden-hjoernet", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "setFrameTopLeftPoint(boksHjoerne(synlig: f, bredde: frame.width))", + "setFrameTopLeftPoint(NSPoint(x: f.minX - 500, y: f.maxY))" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5g" + }, + { + "navn": "Z1-maalet-bindes-ikke", + "fil": "mcp-server/index.js", + "par": [ + [ + " && (tool.tier !== TIER.READ || (name === 'computer_screenshot' && args.redact === false))) args[BUNDET] = koerendeMaal;", + " && false) args[BUNDET] = koerendeMaal;" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "1a" + }, + { + "navn": "Z2-hjaelperkald-uden-binding", + "fil": "mcp-server/index.js", + "par": [ + [ + "const appArg = (args) => (args[BUNDET] ? `=${args[BUNDET]}` : String(args.app));", + "const appArg = (args) => String(args.app);" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "1a" + }, + { + "navn": "Z3-lukket-program-bindes", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (args.app && koerendeMaal && koerendeMaal === targetBundleId\n", + " if (args.app && targetBundleId && (koerendeMaal = targetBundleId)\n" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "2" + }, + { + "navn": "Z4-serveren-bundet-falder-til-navne", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (w.startsWith('=')) return (w.length > 1 && apps.find(a => (a.bundleId || '') === w.slice(1))) || null;\n", + "" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1b.1" + }, + { + "navn": "Z5-hjaelperen-bundet-uden-tjek", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "guard !id.isEmpty, findesPraecist(id) else { return nil }", + "guard !id.isEmpty else { return nil }" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2c.6" + }, + { + "navn": "Z6-aktiv-kun-for-den-ene-proces", + "fil": "mcp-server/helper.js", + "par": [ + [ + " return { bundleId: hit.bundleId || null, active: !!hit.active || !!aktivProces, name: (aktivProces || hit).name };", + " return { bundleId: hit.bundleId || null, active: !!hit.active, name: (aktivProces || hit).name };" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "3" + }, + { + "navn": "Z7-boksen-uden-hvor-og-hvor-laenge", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " for f in v.fakta { linjer += ombryd(f, bredde: BOKS_BREDDE_TEGN) }\n", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "2h" + }, + { + "navn": "Z8-ingen-pause-efter-nyt-spoergsmaal", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "sidenNytSpoergsmaal >= BOKS_PAUSE && !touchIdIGang", + "!touchIdIGang" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6c" + }, + { + "navn": "Z9-knapper-aktive-under-touch-id", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " sidenNytSpoergsmaal >= BOKS_PAUSE && !touchIdIGang\n", + " sidenNytSpoergsmaal >= BOKS_PAUSE\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6c" + }, + { + "navn": "Z10-deny-bliver-done", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "case \"Deny\", \"I won't do this\": return .afvis(n)", + "case \"Deny\", \"I won't do this\": return .gjort(n)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6a" + }, + { + "navn": "Z11-klik-fra-aeldre-visning", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "guard aktive, let n = knapNonce, n == visteNonce else { return .intet }", + "guard let n = knapNonce else { return .intet }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6a" + }, + { + "navn": "Z12-boksen-svarer-paa-sidste-spoergsmaal", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "let foerste = aabne.first", + "let foerste = aabne.last" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5m" + }, + { + "navn": "Z13-touch-id-slipper-aldrig-knapperne", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " self?.boks.touchIdIGang = false\n", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5k" + }, + { + "navn": "Z14-take-me-there-forkert", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "case \"Take me there\": return .hentFrem(n)", + "case \"Take me there\": return .afvis(n)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6a" + }, + { + "navn": "Z15-program-uden-id-kan-ikke-naas", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + " if let b = bundleId, !b.isEmpty { return b }\n return \"pid:\\(pid)\"", + " return bundleId ?? \"\"" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2d" + }, + { + "navn": "Z16-filter-uden-pid", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " Navne.id(bundleId: a.bundleIdentifier, pid: a.processIdentifier) == scope\n", + " a.bundleIdentifier == scope\n" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6g" + }, + { + "navn": "W1-hjaelperen-glemmer-bindingen", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + "AX.kunPraecistId = oversatArgv.bundet\n", + "" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6h" + }, + { + "navn": "W2-bundet-opslag-falder-til-navne", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if kunPraecistId { return (alle.first { $0.bundleIdentifier == hvad }, false) }\n", + "" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6h" + }, + { + "navn": "W3-bundet-opstart-falder-til-disken", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + "if o.tvetydig || kunPraecistId { return (nil, false) }", + "if o.tvetydig { return (nil, false) }" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "6h" + }, + { + "navn": "W4-sendeporten-ubundet", + "fil": "mcp-server/index.js", + "par": [ + [ + "callHelper(['samtale', '--app', appVaerdi(args, bid), '--title-only']", + "callHelper(['samtale', '--app', bid, '--title-only']" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "4" + }, + { + "navn": "W5-pid-ikke-reserveret", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (/^pid:\\d+$/.test(w)) return apps.find(a => !a.bundleId && String(a.pid) === w.slice(4)) || null;\n", + "" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1c.1" + }, + { + "navn": "W6-touch-id-nej-bliver-ja", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "ok && erLaan ? .laan : .svar(ok)", + "ok && erLaan ? .laan : .svar(true)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6d" + }, + { + "navn": "W7-ok-overskrevet-i-callbacken", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " self?.boks.touchIdIGang = false\n // Et mislykket", + " self?.boks.touchIdIGang = false\n let ok = true\n // Et mislykket" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5c2" + }, + { + "navn": "W8-skaermbillede-ubundet", + "fil": "mcp-server/index.js", + "par": [ + [ + " && (tool.tier !== TIER.READ || (name === 'computer_screenshot' && args.redact === false))) args[BUNDET] = koerendeMaal;", + " && tool.tier !== TIER.READ) args[BUNDET] = koerendeMaal;" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "5" + }, + { + "navn": "V1-serveren-ser-ikke-delt-id", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (viaNavn) return deltId(viaNavn) ? null : viaNavn;", + " if (viaNavn) return viaNavn;" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "6" + }, + { + "navn": "V2-hjaelperen-ser-ikke-delt-id", + "fil": "helper/Sources/cmcp-helper/Navne.swift", + "par": [ + [ + "return delt ? .tvetydig : v", + "return v" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "2a.7" + }, + { + "navn": "V3-delt-id-ogsaa-ved-samme-navn", + "fil": "mcp-server/helper.js", + "par": [ + [ + "a.bundleId === x.bundleId && smaa(a.name || '') !== smaa(x.name || '')", + "a.bundleId === x.bundleId" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1a.9" + }, + { + "navn": "Q1-open-app-uden-praecist-id", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (r?.bundleId !== targetBundleId) {\n", + " if (false) {\n" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "7 open_app med navnet" + }, + { + "navn": "X1-visningstjeneste-uden-usynligt-tegn", + "fil": "mcp-server/helper.js", + "par": [ + [ + "return hits.length === 1 && !deltId(hits[0]) ? hits[0] : null;", + "return hits.length === 1 ? hits[0] : null;" + ] + ], + "proeve": "test/usynlige-navne.mjs", + "forventet": "1a.10" + }, + { + "navn": "Q2-open-app-uden-formkrav", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,119}$/.test(targetBundleId)) {\n", + " if (false) {\n" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "7b" + }, + { + "navn": "T1-stille-laan-uden-stopknap", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " knapper: [TAG_TILBAGE] + stop + resten)\n}\n", + " knapper: stop + resten)\n}\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "7a" + }, + { + "navn": "T2-laan-og-spoergsmaal-uden-stopknap", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " knapper: [TAG_TILBAGE] + stop + resten)\n }\n", + " knapper: stop + resten)\n }\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "7b" + }, + { + "navn": "T3-stopknappen-venter-paa-pausen", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " if knap == TAG_TILBAGE { return .tagTilbage }\n", + " if knap == TAG_TILBAGE && aktive && knapNonce == visteNonce { return .tagTilbage }\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6e" + }, + { + "navn": "T4-laanet-holder-ikke-boksen-fremme", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "slaaetFra: boksSlaaetFra && !bannerKlikAktivt, arbejder: arbejder) {\n", + "slaaetFra: boksSlaaetFra && !bannerKlikAktivt, arbejder: arbejder) && laan == nil {\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5s" + }, + { + "navn": "T5-laanet-ikke-i-boksen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "venter: venter, laan: laan, escStopper: escVirker()),", + "venter: venter, laan: nil, escStopper: escVirker())," + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5n" + }, + { + "navn": "T6-boksens-stopknap-goer-intet", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " case .tagTilbage: tagTilbageNu(kilde: \"take-back-box\")\n", + " case .tagTilbage: break\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5o" + }, + { + "navn": "T7-stopknappen-slaas-fra", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "b.isEnabled = aktiv || b.title == TAG_TILBAGE }", + "b.isEnabled = aktiv }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5p" + }, + { + "navn": "T8-boksen-paa-svaevelaget", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " p.isFloatingPanel = true\n p.level = .statusBar\n", + " p.level = .statusBar\n p.isFloatingPanel = true\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5t" + }, + { + "navn": "C6e-porten-ser-navnet-ikke-id", + "fil": "mcp-server/index.js", + "par": [ + [ + " ? await resolveBundleId(args.app)\n : await frontmostBundleId();\n", + " ? String(args.app)\n : await frontmostBundleId();\n" + ] + ], + "proeve": "test/claims.mjs", + "forventet": "6e" + }, + { + "navn": "K1-signalet-ignoreres", + "fil": "helper/Sources/cmcp-helper/Skrivevagt.swift", + "par": [ + [ + " if sikkerIndtastning { return .sikkertFelt }\n", + "" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6a" + }, + { + "navn": "K2-ukendt-fokus-bliver-ja", + "fil": "helper/Sources/cmcp-helper/Skrivevagt.swift", + "par": [ + [ + " guard let s = fokusSikkert else { return .ukendtFokus }\n", + " guard let s = fokusSikkert else { return .maa }\n" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6a" + }, + { + "navn": "K3-type-uden-signal-foerst", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if skriveDom(fokusSikkert: false, sikkerIndtastning: macosSikkerIndtastning()) == .sikkertFelt {\n", + " if false {\n" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6b" + }, + { + "navn": "K4-paste-uden-signal", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " sikkerIndtastning: macosSikkerIndtastning()) {\n case .maa: break\n", + " sikkerIndtastning: false) {\n case .maa: break\n" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6c" + }, + { + "navn": "K5-paste-ukendt-fokus-tilladt", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " case .ukendtFokus:\n Out.fail(\"could not see where the keyboard focus is, so a password field cannot be ruled out; nothing was pasted.", + " case .ukendtFokus: break\n case .maa where false:\n Out.fail(\"could not see where the keyboard focus is, so a password field cannot be ruled out; nothing was pasted." + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6c" + }, + { + "navn": "K6-signalet-er-ikke-macos", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + "func macosSikkerIndtastning() -> Bool { IsSecureEventInputEnabled() }", + "func macosSikkerIndtastning() -> Bool { false }" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6d" + }, + { + "navn": "F1-tidsudloeb-vaelger-alligevel", + "fil": "helper/Sources/cmcp-helper/Skrivevagt.swift", + "par": [ + [ + " if stoppedeTidligt && index == nil { return .ufuldstaendig }\n", + "" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "7e" + }, + { + "navn": "F2-find-tier-om-tidsudloeb", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " fundSvar[\"stopped_early\"] = true\n", + "" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "7d" + }, + { + "navn": "T9-synlig-uden-laan", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " laan || (sessioner > 0 && !slaaetFra && arbejder)\n", + " sessioner > 0 && !slaaetFra && arbejder\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5r" + }, + { + "navn": "T10-stopknap-i-samme-raekke", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if k == TAG_TILBAGE || k == STOP_ALLE_TITEL { stopRaekke.addArrangedSubview(b) } else { knapper.addArrangedSubview(b) }\n", + " knapper.addArrangedSubview(b)\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5v" + }, + { + "navn": "T11-follow-afgjort-af-orange", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if ny == nil && sessioner.count > 1 {\n", + " if !ind.orange && sessioner.count > 1 {\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5w" + }, + { + "navn": "F3-vaelgTraef-uden-tidsflag", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + "stoppedeTidligt: AX.stoppedeTidligt, index: args.int(\"index\")", + "stoppedeTidligt: false, index: args.int(\"index\")" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "7c" + }, + { + "navn": "K7-cmd-v-skriver-ikke", + "fil": "helper/Sources/cmcp-helper/Skrivevagt.swift", + "par": [ + [ + " if cmd { return tast == \"v\" }\n", + " if cmd { return false }\n" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6e" + }, + { + "navn": "K8-key-uden-skrivevagt", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if kombiSkriverTekst(combo) {\n let fokusPid", + " if false {\n let fokusPid" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6f" + }, + { + "navn": "K9-fokus-set-value-uden-signal", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " || (fokusVej && skriveDom(fokusSikkert: false, sikkerIndtastning: macosSikkerIndtastning()) == .sikkertFelt) {", + " {" + ] + ], + "proeve": "test/skriv-ankommer.mjs", + "forventet": "6g" + }, + { + "navn": "L1-loft-kun-paa-grunden", + "fil": "mcp-server/index.js", + "par": [ + [ + " if ([...laanTekst].length > 280) return errorResult(", + " if ([...grund].length > 280) return errorResult(" + ] + ], + "proeve": "test/skaerm-laan.mjs", + "forventet": "9c" + }, + { + "navn": "T12-boksens-lag-saettes-udefra", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " boks.vis(boksIndhold(arbejder: arbejdende, tilsluttede: s.count, venter: venter, laan: laan, escStopper: escVirker()),", + " boks.level = .floating\n boks.vis(boksIndhold(arbejder: arbejdende, tilsluttede: s.count, venter: venter, laan: laan, escStopper: escVirker())," + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5u" + }, + { + "navn": "T13-skjult-ikon-viser-ikke-boksen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if NSApp.isHidden { NSApp.unhideWithoutActivation() }\n", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5y" + }, + { + "navn": "T14-skjult-ikon-genvises-ikke", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if !boks.isVisible || NSApp.isHidden { boks.placer(); visBoksen(boks) }", + " if !boks.isVisible { boks.placer(); visBoksen(boks) }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5y" + }, + { + "navn": "T15-follow-vindue-i-skjult-ikon", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " visBoksen(panel) // frem", + " panel.orderFrontRegardless() // frem" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5y" + }, + { + "navn": "B1-klik-uden-felt-58", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + "if let v = vindue, tilPid != nil { d?.setIntegerValueField(FELT_VINDUE, value: Int64(v)); d?.setIntegerValueField(FELT_VINDUESPUNKT, value: 1) }", + "if let v = vindue, tilPid != nil { d?.setIntegerValueField(FELT_VINDUE, value: Int64(v)) }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "4a" + }, + { + "navn": "B2-klik-uden-binding", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if let bundet = args.int(\"window\"), bundet != v {", + " if let bundet = args.int(\"window\"), bundet != v, false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "4b" + }, + { + "navn": "B3-at-paa-hele-systemet", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " let sys = appPid.map { AXUIElementCreateApplication($0) } ?? AXUIElementCreateSystemWide()", + " let sys = AXUIElementCreateSystemWide()" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "4c" + }, + { + "navn": "B4-porten-doemmer-oeverste-vindue", + "fil": "mcp-server/index.js", + "par": [ + [ + " try { appKlik = await callHelper(['at', '--app', appVaerdi(args, targetBundleId), '--x'", + " try { appKlik = await callHelper(['at', '--x'" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "8a" + }, + { + "navn": "B5-klik-ikke-bundet", + "fil": "mcp-server/index.js", + "par": [ + [ + " ...(Number.isInteger(args[KLIK_VINDUE]) ? ['--window', String(args[KLIK_VINDUE])] : []), ...menuFlag(args)]);", + " ...menuFlag(args)]);" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "8b" + }, + { + "navn": "B6-farlig-knap-doemt-paa-oeverste", + "fil": "mcp-server/index.js", + "par": [ + [ + " const d = appKlik || await callHelper(['at', '--x', String(args.x), '--y', String(args.y)], { timeout: 8000 });\n if (!d?.found) return true;", + " const d = await callHelper(['at', '--x', String(args.x), '--y', String(args.y)], { timeout: 8000 });\n if (!d?.found) return true;" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "8c" + }, + { + "navn": "B7-punkt-uden-vindue-klikkes", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!appKlik?.found || !Number.isInteger(appKlik.window_number)) {", + " if (false) {" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "8d" + }, + { + "navn": "C1-programbillede-uden-kilderamme", + "fil": "helper/Sources/cmcp-helper/Capture.swift", + "par": [ + [ + " cfg.sourceRect = CGRect(x: 0, y: 0, width: display.width, height: display.height)\n", + "\n" + ] + ], + "proeve": "test/billede-placering.mjs", + "forventet": "5" + }, + { + "navn": "C2-menuklik-bagved-sendes", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (baggrund() && !laanAktivt() && (String(args.button || 'left') !== 'left' || MENU_ROLLER.has(appKlik.role))) {", + " if (false) {" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9a" + }, + { + "navn": "C3-rullemenu-ikke-menurolle", + "fil": "mcp-server/index.js", + "par": [ + [ + "const MENU_ROLLER = new Set(['AXPopUpButton', 'AXMenuButton', 'AXComboBox', 'AXMenuBarItem']);", + "const MENU_ROLLER = new Set(['AXMenuButton', 'AXComboBox', 'AXMenuBarItem']);" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9b" + }, + { + "navn": "C4-menunote-tavs", + "fil": "mcp-server/index.js", + "par": [ + [ + "const menuNote = (r) => (r?.menu_opened ? ` A menu opened on the person's screen: ${r.why}`", + "const menuNote = (r) => (false ? ''" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9e" + }, + { + "navn": "C5-menunet-kaldes-ikke", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if klikMenuVagt, let pid = klikPid, let menu = Skaerm.lukNyMenu(pid: pid, foer: menuerFoer) {", + " if klikMenuVagt, let pid = klikPid, false, let menu = Skaerm.lukNyMenu(pid: pid, foer: menuerFoer) {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5d" + }, + { + "navn": "C6-rul-uden-punkt", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (args.app && !punkt) {", + " if (false) {" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "10a" + }, + { + "navn": "C7-rul-uden-sted", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if let p = punkt { e?.location = p }\n", + "\n" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "6d" + }, + { + "navn": "C8-rul-uden-vindue", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " Input.scroll(dx: args.int(\"dx\") ?? 0, dy: args.int(\"dy\") ?? 0, tilPid: rulPid, punkt: rulPunkt, vindue: rulVindue)", + " Input.scroll(dx: args.int(\"dx\") ?? 0, dy: args.int(\"dy\") ?? 0, tilPid: rulPid, punkt: rulPunkt, vindue: nil)" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "6e" + }, + { + "navn": "C9-laerred-altid-2x", + "fil": "helper/Sources/cmcp-helper/Capture.swift", + "par": [ + [ + " cfg.width = display.width * skala\n", + " cfg.width = display.width * 2\n" + ] + ], + "proeve": "test/billede-placering.mjs", + "forventet": "5b" + }, + { + "navn": "C10-rulpunkt-uden-app-sendes", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!args.app && args.x !== undefined) return errorResult(", + " if (false) return errorResult(" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "10c" + }, + { + "navn": "C11-halvt-rulpunkt-kasseres", + "fil": "mcp-server/index.js", + "par": [ + [ + " if ((args.x !== undefined) !== (args.y !== undefined)) return errorResult(", + " if (false) return errorResult(" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "10d" + }, + { + "navn": "C12-tvetydigt-gaettes", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if mindst.forfaedre.contains(t.el) || t.forfaedre.contains(mindst.el) { continue }\n return nil\n", + " if mindst.forfaedre.contains(t.el) || t.forfaedre.contains(mindst.el) { continue }\n continue\n" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "1g" + }, + { + "navn": "C13-tidsudloeb-giver-bedste", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if Date().timeIntervalSince(start) > frist { return nil }\n guard set.insert(el).inserted else { continue }", + " if Date().timeIntervalSince(start) > frist { break }\n guard set.insert(el).inserted else { continue }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "1e" + }, + { + "navn": "C14-andet-vindue-doemmes", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if let pid = appPid, let ramme = vinduesRamme, !AX.tilhoerer(el!, pid: pid, vindue: ramme) {", + " if let pid = appPid, let ramme = vinduesRamme, false, !AX.tilhoerer(el!, pid: pid, vindue: ramme) {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "1f" + }, + { + "navn": "C15-tryk-paa-rullemenu-bagved", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!trykDom.afvis && menuVagt() && MENU_ROLLER.has(trykDom.rolle)) {", + " if (false) {" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9f" + }, + { + "navn": "C16-menuvagt-sendes-ikke", + "fil": "mcp-server/index.js", + "par": [ + [ + "const menuFlag = (args) => (args.app && menuVagt() ? ['--menu-vagt'] : []);", + "const menuFlag = (args) => [];" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9h" + }, + { + "navn": "C17-hjaelper-trykker-menurolle", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if pressMenuVagt, let rolle = first.dict[\"role\"] as? String, Skaerm.menuRoller.contains(rolle) {", + " if pressMenuVagt, false, let rolle = first.dict[\"role\"] as? String, Skaerm.menuRoller.contains(rolle) {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5i" + }, + { + "navn": "C18-tast-paa-menufokus", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if tastMenuVagt, let pid = tastPid, let rolle = AX.fokusRolle(pid: pid), Skaerm.menuRoller.contains(rolle) {", + " if tastMenuVagt, false, let pid = tastPid, let rolle = AX.fokusRolle(pid: pid), Skaerm.menuRoller.contains(rolle) {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5i" + }, + { + "navn": "C19-escape-uden-genkontrol", + "fil": "helper/Sources/cmcp-helper/Skaerm.swift", + "par": [ + [ + " if stadig.intersection(nye).isEmpty {", + " if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5h" + }, + { + "navn": "C20-alle-hoeje-lag-er-menuer", + "fil": "helper/Sources/cmcp-helper/Skaerm.swift", + "par": [ + [ + "(w[kCGWindowLayer as String] as? Int) == lag else { return nil }", + "(w[kCGWindowLayer as String] as? Int ?? 0) >= lag else { return nil }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5h" + }, + { + "navn": "C21-menunote-tavs-ved-tryk", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (r?.menu_opened) tr.content.push({ type: 'text', text: menuNote(r).trim() });", + " if (false) tr.content.push({ type: 'text', text: menuNote(r).trim() });" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9j" + }, + { + "navn": "C22-klik-giver-ikke-tilbage", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if (tilbage[\"took_screen\"] as? Bool) == true { klikMaal.merge(tilbage) { _, ny in ny } }", + " if false { klikMaal.merge(tilbage) { _, ny in ny } }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "4d" + }, + { + "navn": "C23-ukendt-menustatus-tabes", + "fil": "mcp-server/index.js", + "par": [ + [ + " : r?.menu_check === 'unknown' ? ` Note: ${r.why}.` : '');", + " : '');" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9l" + }, + { + "navn": "C24-tvetydig-sender-i-ring", + "fil": "mcp-server/index.js", + "par": [ + [ + " const inde = appKlik?.why && !/not inside any window/.test(appKlik.why);", + " const inde = false;" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "8f" + }, + { + "navn": "C25-tekstnet-efter-retur", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " // laget ovenfor saetter en vaerdi og kan ikke aabne en menu; den har ikke brug for nettet.)\n if skrivMenuVagt, let pid = skrivPid, let menu = Skaerm.lukNyMenu(pid: pid, foer: skrivMenuerFoer) { skrivMaal.merge(menu) { _, ny in ny } }\n", + " // laget ovenfor saetter en vaerdi og kan ikke aabne en menu; den har ikke brug for nettet.)\n" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5j" + }, + { + "navn": "C26-tastnet-aldrig", + "fil": "helper/Sources/cmcp-helper/Skaerm.swift", + "par": [ + [ + " return [\"space\", \"return\", \"enter\", \"up\", \"down\"].contains(dele.last ?? \"\")", + " return false" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "5k" + }, + { + "navn": "C27-anden-app-frem-tavs", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " !Skaerm.menneskeRoerteNetop() {\n klikMaal[\"took_screen\"] = true", + " false {\n klikMaal[\"took_screen\"] = true" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "4e" + }, + { + "navn": "C28-ukendt-lover-intet-frem", + "fil": "mcp-server/index.js", + "par": [ + [ + " : r?.menu_check === 'unknown' ? ' The pointer stayed where the person left it.'\n", + "\n" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9m" + }, + { + "navn": "W1-webfelt-uden-fokus", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if let e = efter, e != sv, AX.fokuserOgSaet(t.el, sv) {", + " if let e = efter, e != sv, false {" + ] + ], + "proeve": "test/web-bagved.mjs", + "forventet": "7" + }, + { + "navn": "P3-rullemenu-vej-slaaet-fra", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if role == \"AXPopUpButton\" {", + " if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P4-afvisning-uden-rullemenuvej", + "fil": "mcp-server/index.js", + "par": [ + [ + "Refused: ${String(args.button || 'left') !== 'left' ? 'a right or middle click' : `a click on this ${appKlik.role}`} opens a menu, and a menu from an app behind is drawn on the person's screen and holds their mouse and keyboard until it closes. Nothing was sent. To choose in a pop-up, use computer_set_value on it", + "Refused: ${String(args.button || 'left') !== 'left' ? 'a right or middle click' : `a click on this ${appKlik.role}`} opens a menu, and a menu from an app behind is drawn on the person's screen and holds their mouse and keyboard until it closes. Nothing was sent. To choose in a pop-up, use the screen loan" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9g" + }, + { + "navn": "RB1-efterkontrol-vaek", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if let e = efter, e != sv {\n Out.fail(\"the element said yes and did not change", + " if false, let e = efter, e != sv {\n Out.fail(\"the element said yes and did not change" + ] + ], + "proeve": "test/stille-vej.mjs", + "forventet": "efterkontrollen staar i set-value" + }, + { + "navn": "D1-graat-element-trykkes", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if let til = AX.attr(first.el, kAXEnabledAttribute as String) as? Bool, !til {", + " if false, let til = AX.attr(first.el, kAXEnabledAttribute as String) as? Bool, !til {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "8c" + }, + { + "navn": "P1-rullemenu-uden-rovagt", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if roIgen < 2 {", + " if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P2-rullemenu-lukkes-ikke", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " switch ventLukket(kode == nil ? 0.6 : 0.05) {", + " switch Optional(true) as Bool? {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P5-rullemenu-dublet-gaettes", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " } else if traef.count > 1 {", + " } else if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P6-rullemenu-graat-punkt", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " } else if let til = attr(traef[0].e, kAXEnabledAttribute as String) as? Bool, !til {", + " } else if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P7-rullemenu-uden-genkontrol", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " } else if stille() < Date().timeIntervalSince(start) {", + " } else if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P8-rullemenu-lukning-paastaas", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " ekstra[\"menu_closed\"] = false\n", + " ekstra[\"menu_closed\"] = true\n" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P9-rullemenu-svar-uden-lukning", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " Out.ok(([\"set\": true, \"verified\": true, \"length\": sv.count, \"element\": t.dict, \"chosen_in_popup\": true] as [String: Any]).merging(rs.ekstra) { a, _ in a })", + " Out.ok(([\"set\": true, \"verified\": true, \"length\": sv.count, \"element\": t.dict, \"chosen_in_popup\": true] as [String: Any]))" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P10-rullemenu-ikke-set-kaldes-lukket", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if punkter.isEmpty {\n // R32 N1", + " if false {\n // R32 N1" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P11-rullemenu-uset-som-aaben", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if !setAaben {\n ekstra[\"menu_opened\"] = false", + " if false {\n ekstra[\"menu_opened\"] = false" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P12-rullemenu-travlt-program", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if Date().timeIntervalSince(t0) > 0.2 {", + " if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P14-escape-uden-genopslag", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if aabenNu() == true {\n for ned", + " if true {\n for ned" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P15-rullemenu-uden-skaerm-i-loggen", + "fil": "mcp-server/index.js", + "par": [ + [ + " return medEffekt(medSkaerm(textResult(r), r), r?.verified === true ? 'verified' : 'sent');", + " return medEffekt(textResult(r), r?.verified === true ? 'verified' : 'sent');" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9n" + }, + { + "navn": "P13-rullemenu-uden-value-now", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " ekstra[\"value_now\"] = string(el, kAXValueAttribute as String) ?? NSNull()\n if punkter.count > alle.count {", + " if punkter.count > alle.count {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P17-blind-vindueliste-som-vidne", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if listeVidne, let foer, let nu = Skaerm.menuer(pid) {", + " if let foer, let nu = Skaerm.menuer(pid) {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P18-ukendt-stopper-vagten", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " guard let a = aabenNu() else { ukendt = true; usleep(50_000); continue }", + " guard let a = aabenNu() else { ukendt = true; break }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P19-fejlet-opslag-som-tomt", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " guard fejl == .success, let boern = v as? [AXUIElement] else { return nil }", + " guard fejl == .success, let boern = v as? [AXUIElement] else { return false }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P20-statusfelter-skaeres-af", + "fil": "mcp-server/index.js", + "par": [ + [ + " ? '\\n\\n' + ekstraTekst(err.extra)", + " ? '\\n\\n' + JSON.stringify(err.extra, null, 2).slice(0, 1200)" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9o" + }, + { + "navn": "P16-ax-vidne-ogsaa-naar-lukket-viser-punkter", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if axFoerAaben == false {\n let a = aabenAX()", + " if true {\n let a = aabenAX()" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P21-fejlet-rolle-som-ikke-menu", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " guard let rm = rolle(m) else { return nil }", + " guard let rm = rolle(m) else { continue }" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P22-ax-lukket-uden-at-have-set-den-aaben", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " ax = (a == true || axSaa) ? a : nil", + " ax = a" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P23-uden-stilhed-foer-trykket", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " if stille() < Date().timeIntervalSince(indgang) {", + " if false {" + ] + ], + "proeve": "test/baggrund-mus.mjs", + "forventet": "7c" + }, + { + "navn": "P24-lang-vaerdi-skaeres-midt-i", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (tekst().length > loft) { ud = kort(ud, 80); ud.values_cut = true; }\n", + "\n" + ] + ], + "proeve": "test/bundet-maal.mjs", + "forventet": "9p" + }, + { + "navn": "B1-banner-fra-som-standard", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " d.object(forKey: \"banner\") == nil ? true : d.bool(forKey: \"banner\")", + " d.object(forKey: \"banner\") == nil ? false : d.bool(forKey: \"banner\")" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9a" + }, + { + "navn": "B2-banner-faar-knap", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " c.title = \"An agent needs you\"", + " c.title = \"An agent needs you\"\n c.categoryIdentifier = \"svar\"" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9c" + }, + { + "navn": "B4-banner-kun-naar-slaaet-til", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " guard bannerTil() else { return }\n let c = UNMutableNotificationContent()", + " guard UserDefaults.standard.bool(forKey: \"banner\") else { return }\n let c = UNMutableNotificationContent()" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9b" + }, + { + "navn": "S3b1-senere-svar-hentes-ikke", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " if (udestaaende && udestaaende.noegle === noegle) return hentUdestaaende(timeoutSec);\n", + "\n" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "7 " + }, + { + "navn": "S3b3-kaldet-venter-hele-fristen", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " const vindue = Math.min(timeoutSec, vindueSek(), Math.max(1, Math.floor(restMs() / 1000)));\n", + " const vindue = timeoutSec;\n" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "1a" + }, + { + "navn": "S3b4-ja-bruges-flere-gange", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " case 'ja':\n u.status = 'brugt';\n", + " case 'ja':\n" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "7 " + }, + { + "navn": "S3b2-noegle-uden-maal", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), ren(target), kind, targetBundle ?? null, h]);", + " : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), kind, targetBundle ?? null, h]);" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "6 " + }, + { + "navn": "R39a-ja-bruges-af-flere", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " case 'ja':\n u.status = 'brugt';\n", + " case 'ja':\n" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "7 " + }, + { + "navn": "R39b-ja-bundet-til-teksten", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), ren(target), kind, targetBundle ?? null, h]);", + " : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), ren(target), kind, targetBundle ?? null]);" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "9 " + }, + { + "navn": "R39c-genkald-faar-ikke-besked", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " for (const l of [...u.venteliste]) l();\n };", + " };" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "8 " + }, + { + "navn": "R39d-lukket-ikon-gemmes", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " u.status = (svar.udloeb || svar.ikkeKoerer || svar.ikonLukket) ? 'slut'", + " u.status = (svar.udloeb || svar.ikkeKoerer) ? 'slut'" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "10 " + }, + { + "navn": "R39e-slags-ikke-i-noeglen", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " return kind === 'goer-selv'\n ? JSON.stringify([session ?? null, ren(client), kind, targetBundle ?? null, h ?? ren(text).trim()])\n : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), ren(target), kind, targetBundle ?? null, h]);", + " return false\n ? null\n : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), ren(target), targetBundle ?? null, h]);" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "11a " + }, + { + "navn": "R39f-omfang-ikke-i-noeglen", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(scope), ren(target), kind, targetBundle ?? null, h]);", + " : JSON.stringify([session ?? null, ren(client), ren(text).trim(), ren(target), kind, targetBundle ?? null, h]);" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "11b " + }, + { + "navn": "R39g-venter-er-et-afslag", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (handlingVenterStadig()) {\n return errorResult(\n `Waiting: ", + " if (false) {\n return errorResult(\n `Waiting: " + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "12 " + }, + { + "navn": "B3-klik-paa-banner-svarer", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " self.boks.placer(); visBoksen(self.boks)\n }", + " self.afvisNonce(r.notification.request.identifier, kilde: \"banner\")\n }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9d " + }, + { + "navn": "B5-banner-id-er-noncen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "identifier: a.bannerId, content: c", + "identifier: a.s.nonce, content: c" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9e " + }, + { + "navn": "B6-banner-aabner-menuen", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " self.boks.placer(); visBoksen(self.boks)\n }", + " self.item.button?.performClick(nil)\n }" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9d " + }, + { + "navn": "R40a-senere-ja-overskrives", + "fil": "mcp-server/godkend.js", + "par": [ + [ + "{ if (udestaaende === u) udestaaende = null; u.frist = Math.min(u.frist, Date.now() + gemtSek() * 1000); gemte.set(u.noegle, u); }", + "{ if (udestaaende === u) udestaaende = null; u.frist = Math.min(u.frist, Date.now() + gemtSek() * 1000); gemte.clear(); gemte.set(u.noegle, u); }" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "13 " + }, + { + "navn": "R40b-bannerklik-vinder-over-fravalg", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if boksSlaaetFra { bannerKlik = nil }\n", + "\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9f " + }, + { + "navn": "R40c-gemt-ja-lever-hele-levetiden", + "fil": "mcp-server/godkend.js", + "par": [ + [ + "u.frist = Math.min(u.frist, Date.now() + gemtSek() * 1000); gemte.set", + "gemte.set" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "14 " + }, + { + "navn": "R40d-andet-kald-er-et-afslag", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " return venterStadig({ ok: false, andetKald: true, grund:", + " return ({ ok: false, grund:" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "7b " + }, + { + "navn": "R40e-spaerret-banner-skjules", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if bannerTil() && bannerSpaerret { banner.title = ", + " if false { banner.title = " + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "9g " + }, + { + "navn": "S1a1-afbrudt-spoergsmaal-venter", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " glemAfbrydelse = vedAfbrydelse(() => { if (!kaldetSvaret) slut(", + " glemAfbrydelse = (() => () => {})(() => { if (!kaldetSvaret) slut(" + ] + ], + "proeve": "test/afbrydelse.mjs", + "forventet": "A1 " + }, + { + "navn": "S1a2-laasen-venter-trods-afbrydelse", + "fil": "mcp-server/index.js", + "par": [ + [ + " }, Math.min(60_000, Math.max(0, restMs())), kaldAfbrudt);", + " }, Math.min(60_000, Math.max(0, restMs())));" + ] + ], + "proeve": "test/afbrydelse.mjs", + "forventet": "B3 " + }, + { + "navn": "S1a3-intet-sidste-tjek", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldAfbrudt()) {\n stopgrund = kaldStoppet() ?", + " if (false) {\n stopgrund = kaldStoppet() ?" + ] + ], + "proeve": "test/afbrydelse.mjs", + "forventet": "C " + }, + { + "navn": "S1a4-signal-foelger-ikke-kaldet", + "fil": "mcp-server/index.js", + "par": [ + [ + "() => haandterKald(request), extra?.signal ?? null));", + "() => haandterKald(request), null));" + ] + ], + "proeve": "test/afbrydelse.mjs", + "forventet": "B2 " + }, + { + "navn": "S1b1-venter-ikke-paa-aktivt-program", + "fil": "mcp-server/index.js", + "par": [ + [ + " while ((maal === undefined || maal?.active) && Date.now() < slutV && !kaldAfbrudt()) {", + " while (false) {" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V1 " + }, + { + "navn": "S1b4-laanet-venter-5-s", + "fil": "mcp-server/index.js", + "par": [ + [ + " const slut = Date.now() + Math.min(ventSek() * 1000, restMs());", + " const slut = Date.now() + Math.min(5000, restMs());" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V4 " + }, + { + "navn": "S1b2-ventende-lagt-i-koeen", + "fil": "mcp-server/index.js", + "par": [ + [ + " target: maal.bundleId, decision: 'waiting', reason: grund0 });", + " target: maal.bundleId, decision: 'waiting', reason: grund0 });\n noterVentende({ tool: name, describe: liveTekst(name, args), mode: currentMode(), reason: grund0 });" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V2b " + }, + { + "navn": "S1b3-ventetid-ikke-afbrydelig", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldAfbrudt()) {\n record({ tool: name, decision: 'cancelled', reason: afbrudtGrund('it stopped waiting for the person') });", + " if (false) {\n record({ tool: name, decision: 'cancelled', reason: afbrudtGrund('it stopped waiting for the person') });" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V3b " + }, + { + "navn": "R42a-intet-gentjek-foer-handling", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (baggrund() && args.app && ROERER_I_PROGRAMMET.has(name) && (!KAN_STILLES.has(name) || kaldErStille(name, args))) {\n const nu0", + " if (false) {\n const nu0" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V5 " + }, + { + "navn": "R42b-ukendt-er-gaaet", + "fil": "mcp-server/index.js", + "par": [ + [ + " while ((maal === undefined || maal?.active) && Date.now() < slutV && !kaldAfbrudt()) {", + " while ((maal?.active) && Date.now() < slutV && !kaldAfbrudt()) {" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V6 " + }, + { + "navn": "R42c-laasen-uden-budget", + "fil": "mcp-server/index.js", + "par": [ + [ + " }, Math.min(60_000, Math.max(0, restMs())), kaldAfbrudt);", + " }, 60_000, kaldAfbrudt);" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V7 " + }, + { + "navn": "R42d-afbrudt-spoerger-alligevel", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " if (kaldAfbrudt()) return resolve({ ok: false, ikkeSpurgt: true, afbrudt: true, grund: afbrudtGrund('nobody was asked') });\n", + "" + ] + ], + "proeve": "test/afbrydelse.mjs", + "forventet": "D2 " + }, + { + "navn": "R42f-ask-user-dialog-efter-afbrydelse", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldAfbrudt()) return errorResult(kaldStoppet() ? 'Stopped: the person pressed «Stop all agents now», so nobody was asked.", + " if (false) return errorResult(kaldStoppet() ? 'Stopped: the person pressed «Stop all agents now», so nobody was asked." + ] + ], + "proeve": "test/afbrydelse.mjs", + "forventet": "D3 " + }, + { + "navn": "R43a-gentjek-uden-budget", + "fil": "mcp-server/index.js", + "par": [ + [ + " const nu0 = await resolveAppStrict(appArg(args), Math.min(5000, restMs()));", + " const nu0 = await resolveAppStrict(appArg(args), 5000);" + ], + [ + " if (restMs() <= 0) {\n stopVenter = true;\n stopgrund = 'this call ran out of time", + " if (false) {\n stopVenter = true;\n stopgrund = 'this call ran out of time" + ] + ], + "proeve": "test/vent-paa-mennesket.mjs", + "forventet": "V9 " + }, + { + "navn": "S2a1-stop-afbryder-ikke-ventende", + "fil": "mcp-server/index.js", + "par": [ + [ + " const n = stopAlleKald();", + " const n = 0;" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1b " + }, + { + "navn": "S2a2-vagten-ser-ikke-stopfilen", + "fil": "mcp-server/index.js", + "par": [ + [ + "watchFile(STOP_STI, { interval: 500, persistent: false }, stopSet);", + "" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1b " + }, + { + "navn": "S2a3-stop-ikke-i-loggen", + "fil": "mcp-server/index.js", + "par": [ + [ + " record({ tool: 'stop-all', decision: 'stopped'", + " ({ tool: 'stop-all', decision: 'stopped'" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1c " + }, + { + "navn": "S2a4-ingen-skrivepause", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (skriver && pauseIGang()", + " if (false && skriver && pauseIGang()" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S2 " + }, + { + "navn": "S2a5-pausen-stopper-laesning", + "fil": "mcp-server/index.js", + "par": [ + [ + " const skriver = tool && (tool.tier !== TIER.READ ||", + " const skriver = tool && (true ||" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S2b " + }, + { + "navn": "S2a6-pausen-udloeber-aldrig", + "fil": "mcp-server/index.js", + "par": [ + [ + "Math.max(pauseSlut, performance.now() + stopSek() * 1000", + "Math.max(pauseSlut, performance.now() + stopSek() * 10000" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S3 " + }, + { + "navn": "S2a7-stop-ligner-klientafbrydelse", + "fil": "mcp-server/godkend.js", + "par": [ + [ + "st.stoppet = true; st.afbryd.abort();", + "st.afbryd.abort();" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1b " + }, + { + "navn": "S2a8-ikonet-beholder-laanet", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "catch { NSSound.beep(); noterKilde(\"stop-all-write-failed\") }\n aktivtLaan?.afslutLaan()", + "catch { NSSound.beep(); noterKilde(\"stop-all-write-failed\") }" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S4 " + }, + { + "navn": "S2a9-stoppet-kald-siger-proev-igen", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldStoppet()) return errorResult(`Stopped: ${grund}. Ask them in the chat before you continue.`);\n", + "" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1b " + }, + { + "navn": "S2a10-stoppet-trin-siger-laan", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (err instanceof HelperError && err.code === 'screen-taken-back' && kaldStoppet()) {", + " if (false) {" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1d " + }, + { + "navn": "S2a11-stop-draeber-ikke-hjaelperen", + "fil": "mcp-server/index.js", + "par": [ + [ + " const draebt = afbrydSkaermKald();\n laanHaandtag?.();", + " const draebt = 0;\n laanHaandtag?.();" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S1d " + }, + { + "navn": "S2a12-boksen-uden-stop-mens-de-arbejder", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "knapper: arbejder.isEmpty ? [] : [\"Follow\", STOP_ALLE_TITEL])", + "knapper: arbejder.isEmpty ? [] : [\"Follow\"])" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "1d " + }, + { + "navn": "S2a13-boksens-stop-venter-paa-nonce", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " if knap == STOP_ALLE_TITEL { return .stopAlle }\n", + "" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "6f " + }, + { + "navn": "S2a14-laanets-boks-uden-stop", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + " let stop = [STOP_ALLE_TITEL]\n", + " let stop: [String] = []\n" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "7a " + }, + { + "navn": "S2a15-boksens-stop-goer-intet", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " case .stopAlle: stopAlleNu(kilde: \"stop-all-box\")", + " case .stopAlle: break" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "5s " + }, + { + "navn": "R45a-pausen-kun-grundvaegt", + "fil": "mcp-server/index.js", + "par": [ + [ + " const skriver = tool && (tool.tier !== TIER.READ || (name === 'computer_record' && args.action === 'start')\n || (name === 'computer_screenshot' && args.redact === false));", + " const skriver = tool && tool.tier !== TIER.READ;" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S5 " + }, + { + "navn": "R45b-optagelsen-koerer-videre", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (optag) Promise.race([o.startLinje,", + " if (false) Promise.race([o.startLinje," + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S6 " + }, + { + "navn": "R45c-spoergsmaal-ikke-trukket-tilbage", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " if (udestaaende?.traekTilbage) udestaaende.traekTilbage();\n", + "" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "15a " + }, + { + "navn": "R45d-gemt-ja-overlever-stop", + "fil": "mcp-server/godkend.js", + "par": [ + [ + " gemte.clear();\n return n;", + " return n;" + ] + ], + "proeve": "test/svar-senere.mjs", + "forventet": "15b " + }, + { + "navn": "R45e-dialogsvar-taeller-efter-stop", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldAfbrudt()) return errorResult(kaldStoppet() ? 'Stopped: the person pressed «Stop all agents now» while the dialog was open", + " if (false) return errorResult(kaldStoppet() ? 'Stopped: the person pressed «Stop all agents now» while the dialog was open" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S7 " + }, + { + "navn": "R45f-fremtidigt-tidsstempel-alle-lag", + "fil": "mcp-server/index.js", + "par": [ + [ + " const fra = Math.min(f.ts, Date.now());", + " const fra = f.ts;" + ], + [ + "- Math.max(0, Date.now() - fra)); };", + "- (Date.now() - fra)); };" + ], + [ + "return { raa, ts, tsVedStart: Number.isFinite(ts) ? Math.min(ts, mtime) : ts, kilde };", + "return { raa, ts, tsVedStart: ts, kilde };" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S8a " + }, + { + "navn": "R45g-intet-gulv", + "fil": "mcp-server/index.js", + "par": [ + [ + "return Math.max(5, Number.isFinite(v) ? v : 120); };", + "return Math.max(0, Number.isFinite(v) ? v : 120); };" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S9 " + }, + { + "navn": "R45h-pausen-paa-vaeguret", + "fil": "mcp-server/index.js", + "par": [ + [ + "const saetPause = (fra) => { pauseSlut = Math.max(pauseSlut, performance.now() +", + "const saetPause = (fra) => { pauseSlut = Math.max(pauseSlut, Date.now() +" + ], + [ + "const pauseIGang = () => performance.now() < pauseSlut;", + "const pauseIGang = () => Date.now() < pauseSlut;" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S10 " + }, + { + "navn": "R45i-spoergsmaalsboks-uden-stop", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "knapper: knapper + (arbejder.isEmpty ? [] : [STOP_ALLE_TITEL]))", + "knapper: knapper)" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "2i " + }, + { + "navn": "R45j-menuen-uden-stop", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if !s.isEmpty || aktivtLaan != nil {\n // S2b-2", + " if false {\n // S2b-2" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S4 " + }, + { + "navn": "R46a-stoppet-billede-udleveres", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (args.redact === false && kaldAfbrudt()) {", + " if (false) {" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S11 " + }, + { + "navn": "R46b-sent-ja-giver-sessionssamtykke", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldStoppet() && verdict.allow && verdict.asked) {\n if (!sessionFoer.global && isSessionGranted()) resetSession();\n if (sessionsProgrammer().some(p => !sessionFoer.programmer.has(p))) glemSessionsProgrammer();\n }\n", + "" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S12 " + }, + { + "navn": "R46c-stop-beholder-sessionssamtykke", + "fil": "mcp-server/index.js", + "par": [ + [ + " resetSession(); glemSessionsProgrammer();\n const draebt = afbrydSkaermKald();", + " const draebt = afbrydSkaermKald();" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S12b " + }, + { + "navn": "R46d-optagestart-siger-recording", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldStoppet()) return errorResult('Stopped: the person pressed «Stop all agents now» while the recording started", + " if (false) return errorResult('Stopped: the person pressed «Stop all agents now» while the recording started" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S13 " + }, + { + "navn": "R46e-done-foer-stop-taeller", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldStoppet()) return errorResult(`Stopped: ${svar.ok ?", + " if (false) return errorResult(`Stopped: ${svar.ok ?" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S14a " + }, + { + "navn": "R46f-ny-stopfil-forkorter-pausen", + "fil": "mcp-server/index.js", + "par": [ + [ + "pauseSlut = Math.max(pauseSlut, performance.now() + stopSek() * 1000", + "pauseSlut = (performance.now() + stopSek() * 1000" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S15 " + }, + { + "navn": "R46g-fremtidigt-stempel-i-gammel-fil", + "fil": "mcp-server/index.js", + "par": [ + [ + "return { raa, ts, tsVedStart: Number.isFinite(ts) ? Math.min(ts, mtime) : ts, kilde };", + "return { raa, ts, tsVedStart: ts, kilde };" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S16 " + }, + { + "navn": "R47a-sent-nej-rydder-nyere-ja", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (kaldStoppet() && verdict.allow && verdict.asked) {", + " if (kaldStoppet()) {" + ], + [ + " if (!sessionFoer.global && isSessionGranted()) resetSession();\n if (sessionsProgrammer().some(p => !sessionFoer.programmer.has(p))) glemSessionsProgrammer();", + " resetSession();\n glemSessionsProgrammer();" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S12c " + }, + { + "navn": "R47b-billedfilen-bliver-liggende", + "fil": "mcp-server/index.js", + "par": [ + [ + " try { unlinkSync(out); } catch { /* ingen fil */ }\n return errorResult(kaldStoppet() ? 'Stopped: the person pressed «Stop all agents now» while the screenshot was taken", + " return errorResult(kaldStoppet() ? 'Stopped: the person pressed «Stop all agents now» while the screenshot was taken" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S11 " + }, + { + "navn": "R47c-stoppet-logges-ok", + "fil": "mcp-server/index.js", + "par": [ + [ + "outcome: result?.isError && kaldAfbrudt() ? (kaldStoppet() ? 'stopped' : 'cancelled') : 'ok',", + "outcome: 'ok'," + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S11 " + }, + { + "navn": "R47d-mtime-kap-i-koerende-server", + "fil": "mcp-server/index.js", + "par": [ + [ + " const fra = Math.min(f.ts, Date.now());", + " const fra = Math.min(f.tsVedStart, Date.now());" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S17 " + }, + { + "navn": "S2b1-traek-blokerer-ikke-stop", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + "case \"drag\":\n Input.blokerStop() // S2b: et stop slipper knappen, foer processen gaar\n", + "case \"drag\":\n" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F5 " + }, + { + "navn": "S2b2-traek-slipper-ikke-under-skridt", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { slipKnap(her, start); return .stoppet }\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b3-traek-slipper-ikke-under-hold", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { slipKnap(start, start); return .stoppet }\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b4-tegn-stopper-ikke", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { return sendt } // S2b: et stop mellem to tegn - aldrig midt i ét\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b5-klik-stopper-ikke", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { return i - 1 }", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b6-frist-uden-sigkill", + "fil": "mcp-server/helper.js", + "par": [ + [ + " try { child.kill('SIGKILL'); } catch { return; }", + " try { } catch { return; }" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F4 " + }, + { + "navn": "S2b7-frist-draeber-ikke", + "fil": "mcp-server/helper.js", + "par": [ + [ + "const fristUr = setTimeout(() => { udloebet = true; draebBlidt(child); }, timeout);", + "const fristUr = setTimeout(() => { udloebet = true; }, timeout);" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F4 " + }, + { + "navn": "S2b8-stop-draeber-haardt", + "fil": "mcp-server/helper.js", + "par": [ + [ + "else { draebBlidt(child); n++; }", + "else { try { child.kill('SIGTERM'); n++; } catch {} }" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b9-klik-foer-foerste", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { return 0 } // R49: et stop under opslagene - intet sendes\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b10-traek-foer-tryk", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { return .ikkeBegyndt } // R49: et stop under opslagene - intet sendes\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b11-tast-efter-stop", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if Input.stopVenter { Out.fail(\"stopped before the key was sent", + " if false { Out.fail(\"stopped before the key was sent" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b12-ax-skriv-efter-stop", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " // R49 (Opus R1): et stop under opslagene ovenfor - intet skrives, heller ikke gennem tilgaengeligheds-laget.\n if Input.stopVenter {", + " // R49 (Opus R1): et stop under opslagene ovenfor - intet skrives, heller ikke gennem tilgaengeligheds-laget.\n if false {" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b13-klikserie-siger-ok", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if klikGjort < klikAntal {\n", + " if false {\n" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b14-intet-sikkerhedsnet", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (argv[0] === 'drag') {\n // R50 (Opus N3)", + " if (false) {\n // R50 (Opus N3)" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F9 " + }, + { + "navn": "S2b15-frist-sletter-svaret", + "fil": "mcp-server/helper.js", + "par": [ + [ + "if (udloebet && !parsed) return reject(", + "if (udloebet) return reject(" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F4b " + }, + { + "navn": "S2b16-krog-skriver-alle-steder", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if attrapStiTilladt(fil) {", + " if true {" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b17-kun-sigterm", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + "[SIGTERM, SIGINT, SIGHUP]", + "[SIGTERM]" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b18-frist-kaldes-laan", + "fil": "mcp-server/helper.js", + "par": [ + [ + " if (udloebet && !child.afbrudtAfLaan && parsed?.ok === false && parsed.code === 'screen-taken-back') {", + " if (false) {" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F4b " + }, + { + "navn": "S2b19-klikserie-foer-nettene", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " // S2b/R49 (Opus R2): et stop foer eller mellem klikkene er ikke «ok» - svaret siger hvor mange der naaede ud.\n // R50 (Opus N2): EFTER nettene ovenfor (menu lukket, forgrund givet tilbage), og maalingen kommer med.\n if klikGjort < klikAntal {", + " // S2b/R49 (Opus R2): et stop foer eller mellem klikkene er ikke «ok» - svaret siger hvor mange der naaede ud.\n // R50 (Opus N2): EFTER nettene ovenfor (menu lukket, forgrund givet tilbage), og maalingen kommer med.\n if false {" + ], + [ + " // Aabnede klikket bagved en menu paa menneskets skaerm, lukkes den igen (Skaerm.lukNyMenu).\n", + " if klikGjort < klikAntal { Out.fail(\"x\", code: \"screen-taken-back\") }\n // Aabnede klikket bagved en menu paa menneskets skaerm, lukkes den igen (Skaerm.lukNyMenu).\n" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b20-slip-uden-tilstandstjek", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " guard CGEventSource.buttonState(.combinedSessionState, button: .left) else { return false }\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b21-traek-slut-uden-tjek", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + " if stopVenter { slipKnap(slut, start); return .stoppet } // R50 (Opus N4): ogsaa i opholdet paa maalet\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b22-ax-uden-sidste-tjek", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " guard !Input.stopVenter else { return nil }\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b23-tast-uden-sidste-tjek", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if Input.stopVenter { tastStoppet = true; return }\n", + "" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b24-nul-klik-koerer-nettene", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if klikGjort == 0 {\n Out.fail(\"stopped before the click", + " if false {\n Out.fail(\"stopped before the click" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F6 " + }, + { + "navn": "S2b25-globalt-esc-sendes", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (name === 'computer_key' && !args.app && erGlobaltEsc) {", + " if (false && name === 'computer_key' && !args.app && erGlobaltEsc) {" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S18 " + }, + { + "navn": "S2b26-esc-altid-registreret", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " if aktivt, escRef == nil {", + " if escRef == nil {" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b27-esc-ikke-ved-laanets-start", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " laanOpdateret() // S2b-2: Esc bliver stoptast, saa laenge laanet varer\n", + "" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b28-agent-kan-sende-punktum", + "fil": "helper/Sources/cmcp-helper/Input.swift", + "par": [ + [ + "\"0\": 29, \"1\": 18,", + "\"period\": 47, \"0\": 29, \"1\": 18," + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b29-genvej-stopper-ikke", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "genveje.vedStop = { ikon.stopAlleNu(kilde: \"stop-all-hotkey\") }", + "genveje.vedStop = { }" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b30-esc-varianter-glider", + "fil": "mcp-server/index.js", + "par": [ + [ + "const escDele = String(args.combo ?? '').toLowerCase().split('+').map(x => x.trim()).filter(Boolean);", + "const escDele = [String(args.combo ?? '').toLowerCase().trim()];" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S18b " + }, + { + "navn": "S2b31-esc-kun-under-eget-laan", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (name === 'computer_key' && !args.app && erGlobaltEsc) {", + " if (name === 'computer_key' && !args.app && laanAktivt() && erGlobaltEsc) {" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S18c " + }, + { + "navn": "S2b32-handler-ikke-tjekket", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " self.handlerOk = handlerOk == noErr\n", + " self.handlerOk = true\n" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b33-boks-lover-esc-altid", + "fil": "helper/Sources/cmcp-status/Tekst.swift", + "par": [ + [ + "(escStopper ? \"; Esc stops every agent and takes it back\" : \"\")", + "\"; Esc stops every agent and takes it back\"" + ] + ], + "proeve": "test/ikon-boks.mjs", + "forventet": "7d " + }, + { + "navn": "S2b34-taster-uden-handler", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "stopRegistreret = self.handlerOk && RegisterEventHotKey", + "stopRegistreret = RegisterEventHotKey" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b35-samtykke-lover-esc-altid", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + "\\(escMulig() ? \" - or with Esc when the box shows it, which also stops every agent for two minutes\" : \"\")", + "\" - or with Esc when the box shows it, which also stops every agent for two minutes\"" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "S2b36-close-foer-lukket", + "fil": "helper/Sources/cmcp-status/main.swift", + "par": [ + [ + " a.lukket = true\n close(k)\n", + " close(k)\n a.lukket = true\n" + ] + ], + "proeve": "test/stop-alle.mjs", + "forventet": "S19 " + }, + { + "navn": "MV1-tryk-paa-rullemenu-bagved", + "fil": "mcp-server/index.js", + "par": [ + [ + " if (!trykDom.afvis && menuVagt() && MENU_ROLLER.has(trykDom.rolle)) {", + " if (false) {" + ] + ], + "proeve": "test/menu-vagt.mjs", + "forventet": "1a" + }, + { + "navn": "MV2-menuvagt-sendes-ikke", + "fil": "mcp-server/index.js", + "par": [ + [ + "const menuFlag = (args) => (args.app && menuVagt() ? ['--menu-vagt'] : []);", + "const menuFlag = (args) => [];" + ] + ], + "proeve": "test/menu-vagt.mjs", + "forventet": "1b" + }, + { + "navn": "MV3-menuvagt-ogsaa-i-forgrund", + "fil": "mcp-server/index.js", + "par": [ + [ + "const menuVagt = () => baggrund() && !laanAktivt();", + "const menuVagt = () => true;" + ] + ], + "proeve": "test/menu-vagt.mjs", + "forventet": "1d" + }, + { + "navn": "MV4-hjaelper-trykker-menurolle", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if pressMenuVagt, let rolle = first.dict[\"role\"] as? String, Skaerm.menuRoller.contains(rolle) {", + " if pressMenuVagt, false, let rolle = first.dict[\"role\"] as? String, Skaerm.menuRoller.contains(rolle) {" + ] + ], + "proeve": "test/menu-vagt.mjs", + "forventet": "3" + }, + { + "navn": "MV5-tast-paa-menufokus", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if tastMenuVagt, let pid = tastPid, let rolle = AX.fokusRolle(pid: pid), Skaerm.menuRoller.contains(rolle) {", + " if tastMenuVagt, false, let pid = tastPid, let rolle = AX.fokusRolle(pid: pid), Skaerm.menuRoller.contains(rolle) {" + ] + ], + "proeve": "test/menu-vagt.mjs", + "forventet": "3" + }, + { + "navn": "MV6-tekst-paa-menufokus", + "fil": "helper/Sources/cmcp-helper/main.swift", + "par": [ + [ + " if skrivMenuVagt, let pid = skrivPid, let rolle = AX.fokusRolle(pid: pid), Skaerm.menuRoller.contains(rolle) {", + " if skrivMenuVagt, false, let pid = skrivPid, let rolle = AX.fokusRolle(pid: pid), Skaerm.menuRoller.contains(rolle) {" + ] + ], + "proeve": "test/menu-vagt.mjs", + "forventet": "3" + }, + { + "navn": "S2b37-menunettets-escape-uden-om-krogen", + "fil": "helper/Sources/cmcp-helper/Skaerm.swift", + "par": [ + [ + " Input.post(CGEvent(keyboardEventSource: nil, virtualKey: 53, keyDown: ned), pid)\n", + " CGEvent(keyboardEventSource: nil, virtualKey: 53, keyDown: ned)?.postToPid(pid)\n" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F11" + }, + { + "navn": "S2b38-paste-cmdv-uden-om-krogen", + "fil": "helper/Sources/cmcp-helper/Accessibility.swift", + "par": [ + [ + " Input.post(ned)\n Input.post(op)\n", + " ned.post(tap: .cghidEventTap)\n op.post(tap: .cghidEventTap)\n" + ] + ], + "proeve": "test/sikker-frigivelse.mjs", + "forventet": "F11" + } +] diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9197a272..0730a315 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -10,7 +10,7 @@ jobs: build-and-test: # macOS-koerer. Der findes ingen anden slags: hele produktet er bygget paa # macOS' egne rammevaerker. - runs-on: macos-14 + runs-on: macos-15 steps: - uses: actions/checkout@v4 @@ -61,12 +61,46 @@ jobs: - name: Revisionskaeden under samtidighed run: node test/audit-chain.mjs + # ⛔ 7/10: den udgivne pakke manglede et modul (tilstede.js) uden at nogen proeve + # saa det - alle koerer fra kildemappen. Kraever ingen rettigheder. + - name: Alt serveren importerer, er med i pakken + run: node test/pakke-filer.mjs + + # 7/10 (punkt L): README'ens log-uddrag er en byte-eksakt del af en committet + # log fra en isoleret koersel, og kaeden holder. Kraever ingen rettigheder. + - name: README'ens log-uddrag er loggen som den er + run: node test/readme-log.mjs + + # 7/10 (punkt S): hoejst én lyd pr. nyt spoergsmaal. Kompilerer ikonets Lyd.swift + # for sig - starter aldrig ikonet. + - name: Ikonets lyd-regel + run: node test/ikon-lyd.mjs + + # 9/10: WhatsApp hedder «\u200eWhatsApp». Serverens og hjaelperens navneregel + # koeres mod samme tabel; Navne.swift kompileres for sig. + - name: Usynlige tegn i programnavne + run: node test/usynlige-navne.mjs + + # 9/10: boksen i hjoernet - indhold, skaerm og Touch ID-vejen. Kompilerer + # Tekst.swift for sig og laeser ikonets --dump-question; starter aldrig ikonet. + - name: Boksen i hjoernet + run: node test/ikon-boks.mjs + + # R17 (9/10): det program porten vurderede, er det handlingen rammer (attrap-hjaelper). + - name: Bundet maal + run: node test/bundet-maal.mjs + + # 7/10 (panel R12, Opus): pluginnet skal pege paa PUBLICERET - den version npm + # serverer. Flettes en gren uden at sync-tal.py har koert efter en udgivelse, + # bliver main roed her, og release.sh nægter at udgive. + - name: Pluginnet peger paa den version npm serverer + run: node test/plugin-manifest.mjs + - name: Det der IKKE er proevet her # En groen koersel maa ikke kunne forveksles med en fuld proeve. - # Tilgaengelighed og Skaermoptagelse kan ikke gives paa en byggekoerer, - # saa alt der roerer en rigtig skaerm staar uproevet indtil et menneske - # koerer ./test/run-all.sh paa en Mac. + # DETTE job roerer ingen rigtig skaerm. Hele suiten koerer i fremmed-mac.yml paa GitHubs + # macOS-koerer, som har Tilgaengelighed og Skaermoptagelse givet (MAALT 27/9). run: | - echo "::warning::Ikke koert her (kraever Tilgaengeligheds- og Skaermoptagelses-adgang):" + echo "::warning::Ikke koert i DETTE job (hele suiten koerer i fremmed-mac.yml paa GitHubs macOS-koerer):" echo "::warning:: server-e2e.mjs, failclosed.mjs, claims.mjs, redaction-proof.sh" echo "::warning::Koer ./test/run-all.sh paa en rigtig Mac foer udgivelse." diff --git a/.github/workflows/fremmed-mac.yml b/.github/workflows/fremmed-mac.yml index 7796c39f..1116c052 100644 --- a/.github/workflows/fremmed-mac.yml +++ b/.github/workflows/fremmed-mac.yml @@ -13,14 +13,50 @@ on: push: branches: ['proeve/**'] workflow_dispatch: + inputs: + full_matrix: + description: 'Kør på hele macOS-matrixen (Intel + Apple Silicon, begge GA-versioner) i stedet for kun macos-15' + type: boolean + default: false + film_pynt: + description: 'Ren optagelse til forsiden (2/10, forside-chatten): notes.txt+almindelige ord, neutral side i stedet for Netflix, normalt skrivebord, Game Center tavs. Rører ALDRIG tjekkene.' + type: boolean + default: false jobs: + # ⛔ A6 (1/10): "macos-14 + macos-15, Intel + Apple Silicon" er forældet ordlyd - + # macos-14 er nu DEPRECATED hos GitHub (højst to GA-versioner ad gangen). + # Nutidens svar er de to AKTIVE GA-versioner, begge arkitekturer. En push + # til en proeve/-gren rører stadig kun macos-15 (uændret hastighed); hele + # matrixen køres kun ved manuelt workflow_dispatch med full_matrix=true - + # ellers 4x koererminutter på hver kørsel under udvikling. + matrix-plan: + runs-on: ubuntu-latest + outputs: + os: ${{ steps.plan.outputs.os }} + steps: + - id: plan + run: | + if [ "${{ inputs.full_matrix }}" = "true" ]; then + echo 'os=["macos-15","macos-15-intel","macos-26","macos-26-intel"]' >> "$GITHUB_OUTPUT" + else + echo 'os=["macos-15"]' >> "$GITHUB_OUTPUT" + fi + fremmed-mac: - runs-on: macos-15 - timeout-minutes: 90 + needs: matrix-plan + strategy: + fail-fast: false + matrix: + os: ${{ fromJSON(needs.matrix-plan.outputs.os) }} + runs-on: ${{ matrix.os }} + # 22-23/10 (0.4): suite ~17 min + scenarier og film + mutantporten med 401 mutanter ~4,5 t. 90 og 180 min + # klippede jobbet midt i porten (koersel 39686798989, 39697547826). 360 = GitHubs loft for macOS-koerere. + timeout-minutes: 360 env: CMCP_FREMMED_MASKINE: '1' CMCP_STATUS_IKON: '0' + CMCP_FILM_PYNT: ${{ inputs.film_pynt && '1' || '' }} steps: - uses: actions/checkout@v4 @@ -30,6 +66,21 @@ jobs: defaults read -g AppleLanguages || true ls /Applications + # Pynt til forsiden (2/10, forside-chatten): kun KAMERAET's billede ændrer + # sig her - rører intet tjek. Alt er best-effort og fejler aldrig bygningen + # (`|| true` overalt): en koerer uden netop den defaults-noegle skal stadig + # kunne koere resten af suiten. + - name: Pynt til forsiden + if: ${{ inputs.film_pynt }} + run: | + PIC=$(ls "/System/Library/Desktop Pictures/"*.heic 2>/dev/null | head -1) + if [ -n "$PIC" ]; then + osascript -e "tell application \"Finder\" to set desktop picture to POSIX file \"$PIC\"" || true + fi + defaults write com.apple.gamed GKAutoSignInEnabled -bool false 2>/dev/null || true + defaults -currentHost write com.apple.notificationcenterui doNotDisturb -bool true 2>/dev/null || true + killall NotificationCenter 2>/dev/null || true + # Samme bygning som udgivelsen: hjaelperen til begge arkitekturer OG # menulinje-ikonet (koersel 3: statusikon-proeven fandt intet ikon). - name: Byg som udgivelsen @@ -50,6 +101,13 @@ jobs: - name: Pillow run: python -m pip install --quiet --disable-pip-version-check pillow + # 1/10 (Gustav: «film hvordan den fungerer»): hvert scenarie og hver + # parallel-koersel optages - kun her, paa GitHubs Mac. + - name: Kamera til filmene + run: | + which ffmpeg || brew install --quiet ffmpeg + ffmpeg -hide_banner -f avfoundation -list_devices true -i "" 2>&1 | grep -i "capture screen" || true + # Uden et menneske er Finder forrest, og produktet roerer (med rette) aldrig # det program mennesket bruger. Et menneske ville arbejde i et andet program # - ogsaa mens suiten koerer (koersel 3: to suiter ramte Finder). @@ -59,6 +117,7 @@ jobs: mcp-server/vendor/cmcp-helper focused || true - name: Hele suiten + id: suite continue-on-error: true run: | export TMPDIR="$RUNNER_TEMP/" @@ -68,10 +127,6 @@ jobs: cp "$RUNNER_TEMP"/cmcp-suite-*.log "$RUNNER_TEMP/suite.log" || true exit $rc - - name: Mutationsbevis for giv-tilbage (proeven tager forgrunden, saa kun her) - continue-on-error: true - run: bash .github/mutant-giv-tilbage.sh - # Uden et menneske er Finder forrest, og produktet roerer (med rette) aldrig # det program mennesket bruger. Et menneske ville arbejde i et andet program. - name: Et menneske arbejder i et andet program @@ -83,8 +138,9 @@ jobs: mcp-server/vendor/cmcp-helper focused || true - name: Brugsscenarierne i baggrunden + id: bg continue-on-error: true - run: CMCP_BRUG=laes,lokal,kamera CMCP_BRUG_RAPPORT="$RUNNER_TEMP/baggrund.json" node test/brugsscenarier.mjs + run: CMCP_FILM="$RUNNER_TEMP/film" CMCP_BRUG=laes,lokal,kamera CMCP_BRUG_RAPPORT="$RUNNER_TEMP/baggrund.json" node test/brugsscenarier.mjs - name: Hvad maskinen viste efter baggrunden if: always() @@ -98,20 +154,94 @@ jobs: sleep 2 - name: Brugsscenarierne i forgrunden + id: fg continue-on-error: true - run: CMCP_BRUG=laes,lokal,kamera CMCP_BRUG_TILSTAND=forgrund CMCP_BRUG_TAG_SKAERMEN=1 CMCP_BRUG_RAPPORT="$RUNNER_TEMP/forgrund.json" node test/brugsscenarier.mjs + run: CMCP_FILM="$RUNNER_TEMP/film" CMCP_BRUG=laes,lokal,kamera CMCP_BRUG_TILSTAND=forgrund CMCP_BRUG_TAG_SKAERMEN=1 CMCP_BRUG_RAPPORT="$RUNNER_TEMP/forgrund.json" node test/brugsscenarier.mjs - name: Hvad maskinen viste efter forgrunden if: always() run: bash .github/diagnose.sh forgrund + # 1/10 (Gustav): 5, 10 og 15 agenter samtidig i baggrunden, mens et menneske + # skriver i TextEdit. Forrest maales hvert 100. ms; teksten tegn for tegn. + - name: Mange agenter samtidig + id: par + continue-on-error: true + run: | + killall Chess Calculator Contacts "System Settings" "Activity Monitor" Notes Safari "Script Editor" 2>/dev/null || true + CMCP_FILM="$RUNNER_TEMP/film" CMCP_PARALLEL_RAPPORT="$RUNNER_TEMP/parallel.json" node test/parallel.mjs + + - name: Filmene + if: always() + uses: actions/upload-artifact@v4 + with: + # Matrix-sikkert navn (A6, 2/10): flere OS-koerere i samme kørsel ville + # ellers kollidere på samme artefakt-navn (upload-artifact@v4 kræver unikt). + name: fremmed-mac-film-${{ matrix.os }} + path: ${{ runner.temp }}/film/ + if-no-files-found: warn + + # B3 (2/10): ét billede pr. use case (før/efter) i en HTML-rapport, linket + # fra PR'en - uden at se alle 19+19 film i fuld længde. Trækker kun billeder + # ud af film "Kamera til filmene" allerede har optaget her. + - name: Kontaktark + if: always() + run: | + python3 scripts/kontaktark.py "${{ runner.temp }}/film" "${{ runner.temp }}/kontaktark" || true + if [ -f "${{ runner.temp }}/kontaktark/index.html" ]; then + echo "### Kontaktark (B3)" >> "$GITHUB_STEP_SUMMARY" + echo "Før/efter-billede pr. use case: se artefaktet **fremmed-mac-kontaktark-${{ matrix.os }}** på denne kørsel." >> "$GITHUB_STEP_SUMMARY" + fi + + - name: Kontaktark-artefakt + if: always() + uses: actions/upload-artifact@v4 + with: + name: fremmed-mac-kontaktark-${{ matrix.os }} + path: ${{ runner.temp }}/kontaktark/ + if-no-files-found: warn + - name: Beviserne if: always() uses: actions/upload-artifact@v4 with: - name: fremmed-mac-beviser + name: fremmed-mac-beviser-${{ matrix.os }} path: | ${{ runner.temp }}/suite.log ${{ runner.temp }}/baggrund.json ${{ runner.temp }}/forgrund.json + ${{ runner.temp }}/parallel.json ${{ runner.temp }}/diag/ + + # 23/10 (0.4): mutantporten (401 mutanter) tager ~4,5 t paa GitHubs Mac. Den laa foer brugsscenarierne, + # saa 90- og 180-minuttersgraensen klippede jobbet foer filmene blev optaget (koersel 39686798989, + # 39697547826). Nu: beviser og film foerst (og uploadet), den lange port sidst. + - name: Mutationsbevis for giv-tilbage (proeven tager forgrunden, saa kun her) + id: mutant + continue-on-error: true + # 29/9 (B7): og hver port-vagt (samtykke, sende-port, skaerm-laan, omveje) + # skal kunne blive roed. bash -e: uden «|| rc» stoppede trinnet ved foerste. + run: | + rc=0 + bash .github/mutant-giv-tilbage.sh || rc=1 + python3 .github/mutant-porte.py || rc=1 + exit $rc + + # ⛔ 28/9: foer var suite, mutant og de to scenarie-koersler alle + # continue-on-error uden en samlende port - en groen koersel beviste + # intet om proeverne. Nu fejler jobbet, hvis en af dem ikke bestod. + # (Beviserne uploades ovenfor med if: always, saa de findes stadig.) + - name: Porten - en fejlet proeve goer hele jobbet roedt + if: always() + run: | + fail=0 + for par in "Suiten:${{ steps.suite.outcome }}" \ + "Mutationsbeviset:${{ steps.mutant.outcome }}" \ + "Baggrunds-scenarier:${{ steps.bg.outcome }}" \ + "Forgrunds-scenarier:${{ steps.fg.outcome }}" \ + "Mange agenter samtidig:${{ steps.par.outcome }}"; do + navn="${par%%:*}"; res="${par##*:}" + echo "$navn: $res" + [ "$res" = "success" ] || { echo "⛔ $navn bestod ikke ($res)"; fail=1; } + done + [ "$fail" = "0" ] && echo "Alle fem porte grønne." || exit 1 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 32b6f897..e94eb4f2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,11 +6,11 @@ name: Release # npm-udgivelsen staar med vilje IKKE her: en udgivelse der sker automatisk # paa et maerke er en udgivelse ingen naaede at se paa foerst. # -# Den bor nu i `udgiv-npm.yml` - workflow_dispatch med et versionsfelt, saa et -# menneske stadig trykker, men der findes ingen token nogen steder (OIDC / -# npm trusted publishing). Grunden: MAALT 21/9 var alle ni tokens paa kontoen -# udloebne, den nyeste efter syv dage, og kontoen kraever en sikkerhedsnoegle -# som andet trin - saa `npm publish` i haanden svarer EOTP uden vej udenom. +# Den bor i `scripts/release.sh`, koert af et menneske i en rigtig terminal: npm +# aabner da godkendelsen med sikkerhedsnoeglen i browseren (MAALT 6/10, npm 10.9.4 +# lib/utils/auth.js - kun med stdin og stdout som terminal). 7/10 (panel R8): den +# tidligere `udgiv-npm.yml` er fjernet - den havde aldrig koert og manglede +# PUBLICERET, tekstsynkroniseringen, deprecate og registret. Én vej, ikke to. on: push: tags: ['v*'] @@ -25,9 +25,25 @@ permissions: jobs: universal: - runs-on: macos-14 + runs-on: macos-15 steps: + # B18 (R1 Astra, 20/10): `tag`-inputtet blev aldrig brugt - en manuel koersel byggede grenen, + # ikke maerket. Nu bygges praecis maerket, og versionen i pakken skal vaere maerkets. - uses: actions/checkout@v4 + with: + # R59 (Astra + Opus): et ukvalificeret navn opløses som GREN før tag - derfor refs/tags/, + # og ved et push bygges netop den commit, der udløste kørslen (github.sha). + ref: ${{ inputs.tag && format('refs/tags/{0}', inputs.tag) || github.sha }} + + - name: Maerket og pakkens version er ens + if: startsWith(github.ref, 'refs/tags/v') || inputs.tag != '' + env: + MAERKE: ${{ inputs.tag || github.ref_name }} + run: | + V="${MAERKE#v}" + P=$(node -p "require('./mcp-server/package.json').version") + [ "$P" = "$V" ] || { echo "::error::maerket $MAERKE, men pakken siger $P"; exit 1; } + echo "bygger $MAERKE = $(git rev-parse HEAD)" - name: Byg, signer og efterproev run: ./scripts/build-release.sh @@ -49,6 +65,18 @@ jobs: python test/redaction-unit.py node test/claims-offline.mjs + # ⛔ 7/10 (panel R8-R9, punkt M): ét-kliks-installation til Claude Desktop - ogsaa + # Team/Enterprise, der ikke har Anthropics indbyggede computer use. Pakken bygges + # af den SAMME binaer som ovenfor og bevises foer den haenges paa udgivelsen: + # den starter fra den udpakkede pakke og svarer med kildens vaerktoejstal. + - name: Byg og bevis MCPB-pakken + run: | + npm i -g @anthropic-ai/mcpb@2.1.2 --no-audit --no-fund + bash scripts/build-mcpb.sh "$RUNNER_TEMP/mcpb" + node test/mcpb-start.mjs "$RUNNER_TEMP/mcpb/computer-mcp.mcpb" + cp "$RUNNER_TEMP/mcpb/computer-mcp.mcpb" computer-mcp.mcpb + shasum -a 256 computer-mcp.mcpb > computer-mcp.mcpb.sha256 + - name: Beskriv hvad der er bygget id: describe run: | @@ -66,9 +94,13 @@ jobs: path: | mcp-server/vendor/cmcp-helper cmcp-helper.sha256 + computer-mcp.mcpb + computer-mcp.mcpb.sha256 - name: Haeng den paa udgivelsen - if: startsWith(github.ref, 'refs/tags/v') + # R59: KUN ved et rigtigt maerke-push. En manuel koersel bygger «uden at udgive» og kan derfor aldrig + # laegge et andet maerkes binaerer oven i en udgivelse (--clobber). + if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v') env: GH_TOKEN: ${{ github.token }} run: | @@ -77,6 +109,8 @@ jobs: gh release upload "${GITHUB_REF_NAME}" \ mcp-server/vendor/cmcp-helper \ cmcp-helper.sha256 \ + computer-mcp.mcpb \ + computer-mcp.mcpb.sha256 \ --clobber - name: Kvittering diff --git a/.github/workflows/side.yml b/.github/workflows/side.yml new file mode 100644 index 00000000..04a4971f --- /dev/null +++ b/.github/workflows/side.yml @@ -0,0 +1,50 @@ +name: side + +# Udgiver computermcp.dev fra docs/ og sætter GitHub-stjernerne ind, mens siden bygges +# (scripts/stjerner.py). Ingen script og intet tredjepartskald i browseren: tallet står +# i HTML'en. Det opdateres ved hvert skub til main og én gang i døgnet. +# +# SLÅET FRA indtil to ting er gjort, begge af en der ejer repoet: +# 1. Settings > Pages > Source = «GitHub Actions» (i dag: «Deploy from a branch», main /docs) +# 2. Repo-variablen SIDE_VIA_ACTIONS = true (Settings > Secrets and variables > Actions > Variables) +# Uden variablen springes jobbet over (ikke rødt), og den klassiske udgivelse kører som før. +# Tilbage: sæt variablen til false og Source tilbage til «Deploy from a branch». + +on: + push: + branches: [main] + paths: ['docs/**', 'scripts/stjerner.py', '.github/workflows/side.yml'] + schedule: + - cron: '17 5 * * *' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + udgiv: + if: vars.SIDE_VIA_ACTIONS == 'true' + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.udgiv.outputs.page_url }} + steps: + - uses: actions/checkout@v4 + - name: Stjernerne ind i siden + env: + GH_TOKEN: ${{ github.token }} + run: | + n=$(gh api "repos/${{ github.repository }}" --jq .stargazers_count) + python3 scripts/stjerner.py docs/index.html "$n" + - uses: actions/configure-pages@v5 + - uses: actions/upload-pages-artifact@v3 + with: + path: docs + - id: udgiv + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/udgiv-npm.yml b/.github/workflows/udgiv-npm.yml deleted file mode 100644 index 605da7cc..00000000 --- a/.github/workflows/udgiv-npm.yml +++ /dev/null @@ -1,111 +0,0 @@ -name: Udgiv til npm - -# ⛔ HVORFOR DEN LIGGER I SIN EGEN FIL, OG HVORFOR DEN IKKE FYRER PAA ET MAERKE -# -# release.yml siger det med vilje: «en udgivelse der sker automatisk paa et -# maerke er en udgivelse ingen naaede at se paa foerst». Den holdning staar -# ved magt. Det trusted publishing fjerner er NOEGLEN, ikke mennesket. -# -# Derfor: workflow_dispatch med et versionsfelt. Et menneske trykker, men -# der findes ingen token nogen steder - hverken i repoet, i GitHub Secrets -# eller paa nogens maskine. -# -# MAALT 21/9-2026, hvorfor det her er noedvendigt: alle ni tokens paa -# npm-kontoen var udloebne. Den nyeste blev lavet 13/9 og doede 20/9 - syv -# dage. npm's egen politik siger 7 dages standard og 90 dages loft, saa en -# token DOER igen. Og kontoen har `tfa: auth-and-writes` med en -# sikkerhedsnoegle som andet trin, saa `npm publish` svarer EOTP og der -# findes ingen engangskode at taste. OIDC har ingen af delene. -# -# ⛔ Publish-trinnet SKAL ligge i denne fil. npm's validering tjekker det -# KALDENDE workflows filnavn, saa et genbrugt workflow virker ikke. -# -# --------------------------------------------------------------------------- -# ENGANGS-OPSAETNING, som kun Gustav kan lave (ét minut, ét sted): -# -# npmjs.com -> @agent360/computer-mcp -> Settings -> Trusted Publisher -# Provider: GitHub Actions -# Repository: Agent360dk/computerMCP -# Workflow: udgiv-npm.yml -# Environment: (tom) -# -# Derefter: ingen token, ingen engangskode, ingen udloeb. Nogensinde. -# --------------------------------------------------------------------------- - -on: - workflow_dispatch: - inputs: - version: - description: 'Version der udgives (skal matche mcp-server/package.json)' - required: true - -permissions: - contents: read - id-token: write # ⛔ den her linje ER trusted publishing. Uden den: ingen OIDC. - -jobs: - udgiv: - # macOS fordi build-release.sh bygger den universelle Swift-binaer - # (arm64 + x86_64) der ligger i pakken. En Linux-koerer kan ikke. - runs-on: macos-14 - - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version: '22' # OIDC kraever Node >= 22.14 - registry-url: 'https://registry.npmjs.org' - - # npm >= 11.5.1 er kravet. Koererens npm er aeldre. - - name: Ny nok npm - run: | - npm i -g npm@latest - echo "npm $(npm -v) · node $(node -v)" - - - name: Versionen skal matche kilden - run: | - K="$(node -p "require('./mcp-server/package.json').version")" - if [ "$K" != "${{ inputs.version }}" ]; then - echo "⛔ kilden siger $K, du bad om ${{ inputs.version }}"; exit 1 - fi - echo " $K" - - # ⛔ ASTRA runde 2 (25/9): denne vej udgav uden release.sh's lokale porte - - # samtykke-kvitteringen, hele suiten paa en Mac, CI-tjekket. Maerket v$V - # saettes KUN af release.sh efter de porte, saa det er beviset paa at de - # koerte - og det skal pege paa praecis den commit der udgives. - - name: Maerket skal findes og pege paa denne commit - env: - V: ${{ inputs.version }} - run: | - T="$(git ls-remote --tags origin "refs/tags/v$V^{}" | cut -f1)" - [ -z "$T" ] && T="$(git ls-remote --tags origin "refs/tags/v$V" | cut -f1)" - if [ -z "$T" ]; then echo "⛔ maerket v$V findes ikke - koer scripts/release.sh $V foerst"; exit 1; fi - if [ "$T" != "$GITHUB_SHA" ]; then echo "⛔ v$V peger paa $T, ikke paa $GITHUB_SHA"; exit 1; fi - echo " v$V = $GITHUB_SHA" - - # ⛔ IKKE en bar npm publish. build-release.sh genskriver - # mcp-server/README.md fra rodens README - uden det sender npm en - # pakkeside der mangler afsnit rodens README har, frosset for evigt. - - name: Byg udgivelsen - run: ./scripts/build-release.sh - - - name: Hjaelperen skal vaere universel - run: | - A="$(lipo -archs mcp-server/vendor/cmcp-helper)" - case "$A" in *x86_64*arm64*|*arm64*x86_64*) echo " $A" ;; - *) echo "⛔ ikke universel: $A"; exit 1 ;; esac - - - name: Paastande der kan proeves uden rettigheder - # CMCP_KRAEV_BINAER=1: her SKAL den sendte binaer findes og maales. - # Uden flaget springer proeven over den og siger UMAALT - fint paa en - # maskine hvor ingen har bygget, men her ville det vaere en blind port. - run: CMCP_KRAEV_BINAER=1 node test/claims-offline.mjs - - - name: Revisionskaeden under samtidighed - run: node test/audit-chain.mjs - - - name: Udgiv - run: cd mcp-server && npm publish --access public - # Ingen NODE_AUTH_TOKEN. Det er hele pointen. diff --git a/.gitignore b/.gitignore index 567460e8..1fce267c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,8 @@ .DS_Store .build/ mcp-server/vendor/ -node_modules/ +# E3 (20/10): uden skraastreg, saa ogsaa et symlink med navnet ignoreres (arbejdskopierne linker til hovedkopiens moduler). +node_modules test/fixture/CMCPFixture.app/Contents/MacOS/CMCPFixture test/fixture/probe test/fixture/secure-window @@ -18,4 +19,9 @@ videos/*/.hyperframes/ # blev TAVST holdt uden for repoet - sitet ville faa en