@ia-qa/self-healing 1.6.6 → 1.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +118 -2
- package/TUTORIAL.md +138 -3
- package/assets/logoKawaiiGreenTiny.png +0 -0
- package/dist/cli/args.js +4 -2
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/diff.d.ts +6 -1
- package/dist/cli/diff.js +114 -4
- package/dist/cli/diff.js.map +1 -1
- package/dist/cli/emit.d.ts +71 -0
- package/dist/cli/emit.js +150 -0
- package/dist/cli/emit.js.map +1 -0
- package/dist/cli/fix.js +36 -0
- package/dist/cli/fix.js.map +1 -1
- package/dist/cli/history.d.ts +12 -0
- package/dist/cli/history.js +95 -0
- package/dist/cli/history.js.map +1 -0
- package/dist/cli/index.js +53 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init.js +32 -0
- package/dist/cli/init.js.map +1 -1
- package/dist/cli/map.js +60 -0
- package/dist/cli/map.js.map +1 -1
- package/dist/cli/run.js +77 -7
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/ui.d.ts +16 -0
- package/dist/cli/ui.js +275 -0
- package/dist/cli/ui.js.map +1 -0
- package/dist/evidence.d.ts +130 -0
- package/dist/evidence.js +232 -0
- package/dist/evidence.js.map +1 -0
- package/dist/history.d.ts +145 -0
- package/dist/history.js +230 -0
- package/dist/history.js.map +1 -0
- package/dist/htmlReport.d.ts +47 -0
- package/dist/htmlReport.js +76 -1
- package/dist/htmlReport.js.map +1 -1
- package/dist/junit.d.ts +53 -0
- package/dist/junit.js +150 -0
- package/dist/junit.js.map +1 -0
- package/dist/mcp/server.js +41 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/ui/icon.d.ts +41 -0
- package/dist/ui/icon.js +115 -0
- package/dist/ui/icon.js.map +1 -0
- package/dist/ui/lock.d.ts +40 -0
- package/dist/ui/lock.js +138 -0
- package/dist/ui/lock.js.map +1 -0
- package/dist/ui/page.d.ts +1 -0
- package/dist/ui/page.js +584 -0
- package/dist/ui/page.js.map +1 -0
- package/dist/ui/registry.d.ts +62 -0
- package/dist/ui/registry.js +139 -0
- package/dist/ui/registry.js.map +1 -0
- package/dist/ui/server.d.ts +67 -0
- package/dist/ui/server.js +604 -0
- package/dist/ui/server.js.map +1 -0
- package/dist/ui/shortcut.d.ts +90 -0
- package/dist/ui/shortcut.js +176 -0
- package/dist/ui/shortcut.js.map +1 -0
- package/package.json +2 -1
- package/skills/ia-qa-heal/SKILL.md +27 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The console's desktop icon.
|
|
3
|
+
*
|
|
4
|
+
* Windows will not take a PNG for a shortcut's `IconLocation` — it wants an
|
|
5
|
+
* `.ico`, an `.exe` or a `.dll`. Converting an image normally means a native
|
|
6
|
+
* encoder, which this package will not take on: `sharp`/`jimp` for one desktop
|
|
7
|
+
* icon would be a heavier dependency than the entire tool.
|
|
8
|
+
*
|
|
9
|
+
* It does not need one. Since Vista, an ICO entry may hold a **PNG verbatim**
|
|
10
|
+
* instead of a BMP, so the conversion is a 22-byte header wrapped around bytes
|
|
11
|
+
* we already have. No decoding, no re-encoding, no dependency, and the result
|
|
12
|
+
* is byte-identical every run.
|
|
13
|
+
*
|
|
14
|
+
* Layout, all little-endian:
|
|
15
|
+
*
|
|
16
|
+
* ICONDIR 6 bytes reserved=0, type=1 (icon), count
|
|
17
|
+
* ICONDIRENTRY 16 bytes w, h, palette=0, reserved=0, planes=1, bpp=32,
|
|
18
|
+
* byteLength, offset
|
|
19
|
+
* …the PNG file itself
|
|
20
|
+
*/
|
|
21
|
+
export declare const ICO_HEADER_BYTES = 22;
|
|
22
|
+
/** Width/height from a PNG's IHDR, which is always the first chunk. */
|
|
23
|
+
export declare function pngSize(png: Buffer): {
|
|
24
|
+
width: number;
|
|
25
|
+
height: number;
|
|
26
|
+
} | null;
|
|
27
|
+
/**
|
|
28
|
+
* Wrap a PNG in an ICO container. Returns null for anything that is not a PNG,
|
|
29
|
+
* rather than producing a file Windows would silently refuse to draw.
|
|
30
|
+
*
|
|
31
|
+
* A dimension of 256 is written as 0 — that is the format's own convention for
|
|
32
|
+
* "256", and it is why sizes above 256 cannot be expressed at all.
|
|
33
|
+
*/
|
|
34
|
+
export declare function pngToIco(png: Buffer): Buffer | null;
|
|
35
|
+
/**
|
|
36
|
+
* The brand logo shipped in the tarball. `assets/` must stay in package.json →
|
|
37
|
+
* `files` or this resolves to a path that only exists in the repo — the same
|
|
38
|
+
* trap `skills/` has (a test pins both).
|
|
39
|
+
*/
|
|
40
|
+
export declare function brandLogoPath(): string;
|
|
41
|
+
export declare function readBrandLogo(): Buffer | null;
|
package/dist/ui/icon.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ICO_HEADER_BYTES = void 0;
|
|
37
|
+
exports.pngSize = pngSize;
|
|
38
|
+
exports.pngToIco = pngToIco;
|
|
39
|
+
exports.brandLogoPath = brandLogoPath;
|
|
40
|
+
exports.readBrandLogo = readBrandLogo;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
/**
|
|
44
|
+
* The console's desktop icon.
|
|
45
|
+
*
|
|
46
|
+
* Windows will not take a PNG for a shortcut's `IconLocation` — it wants an
|
|
47
|
+
* `.ico`, an `.exe` or a `.dll`. Converting an image normally means a native
|
|
48
|
+
* encoder, which this package will not take on: `sharp`/`jimp` for one desktop
|
|
49
|
+
* icon would be a heavier dependency than the entire tool.
|
|
50
|
+
*
|
|
51
|
+
* It does not need one. Since Vista, an ICO entry may hold a **PNG verbatim**
|
|
52
|
+
* instead of a BMP, so the conversion is a 22-byte header wrapped around bytes
|
|
53
|
+
* we already have. No decoding, no re-encoding, no dependency, and the result
|
|
54
|
+
* is byte-identical every run.
|
|
55
|
+
*
|
|
56
|
+
* Layout, all little-endian:
|
|
57
|
+
*
|
|
58
|
+
* ICONDIR 6 bytes reserved=0, type=1 (icon), count
|
|
59
|
+
* ICONDIRENTRY 16 bytes w, h, palette=0, reserved=0, planes=1, bpp=32,
|
|
60
|
+
* byteLength, offset
|
|
61
|
+
* …the PNG file itself
|
|
62
|
+
*/
|
|
63
|
+
exports.ICO_HEADER_BYTES = 22;
|
|
64
|
+
/** Width/height from a PNG's IHDR, which is always the first chunk. */
|
|
65
|
+
function pngSize(png) {
|
|
66
|
+
const SIGNATURE = '89504e470d0a1a0a';
|
|
67
|
+
if (png.length < 24 || png.subarray(0, 8).toString('hex') !== SIGNATURE)
|
|
68
|
+
return null;
|
|
69
|
+
return { width: png.readUInt32BE(16), height: png.readUInt32BE(20) };
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Wrap a PNG in an ICO container. Returns null for anything that is not a PNG,
|
|
73
|
+
* rather than producing a file Windows would silently refuse to draw.
|
|
74
|
+
*
|
|
75
|
+
* A dimension of 256 is written as 0 — that is the format's own convention for
|
|
76
|
+
* "256", and it is why sizes above 256 cannot be expressed at all.
|
|
77
|
+
*/
|
|
78
|
+
function pngToIco(png) {
|
|
79
|
+
const size = pngSize(png);
|
|
80
|
+
if (!size)
|
|
81
|
+
return null;
|
|
82
|
+
if (size.width > 256 || size.height > 256)
|
|
83
|
+
return null;
|
|
84
|
+
const header = Buffer.alloc(exports.ICO_HEADER_BYTES);
|
|
85
|
+
header.writeUInt16LE(0, 0); // reserved
|
|
86
|
+
header.writeUInt16LE(1, 2); // type: icon
|
|
87
|
+
header.writeUInt16LE(1, 4); // one image
|
|
88
|
+
header.writeUInt8(size.width === 256 ? 0 : size.width, 6);
|
|
89
|
+
header.writeUInt8(size.height === 256 ? 0 : size.height, 7);
|
|
90
|
+
header.writeUInt8(0, 8); // palette colours: none, it is truecolour
|
|
91
|
+
header.writeUInt8(0, 9); // reserved
|
|
92
|
+
header.writeUInt16LE(1, 10); // colour planes
|
|
93
|
+
header.writeUInt16LE(32, 12); // bits per pixel
|
|
94
|
+
header.writeUInt32LE(png.length, 14);
|
|
95
|
+
header.writeUInt32LE(exports.ICO_HEADER_BYTES, 18);
|
|
96
|
+
return Buffer.concat([header, png]);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* The brand logo shipped in the tarball. `assets/` must stay in package.json →
|
|
100
|
+
* `files` or this resolves to a path that only exists in the repo — the same
|
|
101
|
+
* trap `skills/` has (a test pins both).
|
|
102
|
+
*/
|
|
103
|
+
function brandLogoPath() {
|
|
104
|
+
// dist/ui/icon.js → package root
|
|
105
|
+
return path.join(__dirname, '..', '..', 'assets', 'logoKawaiiGreenTiny.png');
|
|
106
|
+
}
|
|
107
|
+
function readBrandLogo() {
|
|
108
|
+
try {
|
|
109
|
+
return fs.readFileSync(brandLogoPath());
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=icon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"icon.js","sourceRoot":"","sources":["../../src/ui/icon.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2BA,0BAIC;AASD,4BAkBC;AAOD,sCAGC;AAED,sCAMC;AA5ED,uCAAyB;AACzB,2CAA6B;AAE7B;;;;;;;;;;;;;;;;;;;GAmBG;AAEU,QAAA,gBAAgB,GAAG,EAAE,CAAC;AAEnC,uEAAuE;AACvE,SAAgB,OAAO,CAAC,GAAW;IACjC,MAAM,SAAS,GAAG,kBAAkB,CAAC;IACrC,IAAI,GAAG,CAAC,MAAM,GAAG,EAAE,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACrF,OAAO,EAAE,KAAK,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,EAAE,CAAC;AACvE,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,QAAQ,CAAC,GAAW;IAClC,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAC1B,IAAI,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IACvB,IAAI,IAAI,CAAC,KAAK,GAAG,GAAG,IAAI,IAAI,CAAC,MAAM,GAAG,GAAG;QAAE,OAAO,IAAI,CAAC;IAEvD,MAAM,MAAM,GAAG,MAAM,CAAC,KAAK,CAAC,wBAAgB,CAAC,CAAC;IAC9C,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;IACvC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,aAAa;IACzC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,YAAY;IACxC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAC1D,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,MAAM,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,0CAA0C;IACnE,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,WAAW;IACpC,MAAM,CAAC,aAAa,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,gBAAgB;IAC7C,MAAM,CAAC,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,CAAC,iBAAiB;IAC/C,MAAM,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACrC,MAAM,CAAC,aAAa,CAAC,wBAAgB,EAAE,EAAE,CAAC,CAAC;IAC3C,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;AACtC,CAAC;AAED;;;;GAIG;AACH,SAAgB,aAAa;IAC3B,iCAAiC;IACjC,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,yBAAyB,CAAC,CAAC;AAC/E,CAAC;AAED,SAAgB,aAAa;IAC3B,IAAI,CAAC;QACH,OAAO,EAAE,CAAC,YAAY,CAAC,aAAa,EAAE,CAAC,CAAC;IAC1C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Who is already serving this project's console — `.ia-qa/ui.lock`.
|
|
3
|
+
*
|
|
4
|
+
* The console is opened by clicking something, and people click twice. Without
|
|
5
|
+
* this, the second click either fails on a port collision or starts a *second*
|
|
6
|
+
* server with a *different* token, leaving an orphan process and a browser tab
|
|
7
|
+
* that will 401 the moment the first one is closed. Neither is explicable to
|
|
8
|
+
* the person who clicked.
|
|
9
|
+
*
|
|
10
|
+
* So the lock holds the port and the token, and a launcher that finds a live
|
|
11
|
+
* console simply opens it. The file is a hint, never an authority: it is
|
|
12
|
+
* verified by an actual request before being believed (a lock left behind by a
|
|
13
|
+
* killed process is the normal case, not the exception), and it lives next to
|
|
14
|
+
* the contracts because that is what it is scoped to — one console per project
|
|
15
|
+
* directory, not one per machine.
|
|
16
|
+
*
|
|
17
|
+
* It holds a session token, so it is a file to gitignore, like `_shots/`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const UI_LOCK_FILENAME = "ui.lock";
|
|
20
|
+
export interface UiLock {
|
|
21
|
+
port: number;
|
|
22
|
+
token: string;
|
|
23
|
+
pid: number;
|
|
24
|
+
startedAt: string;
|
|
25
|
+
}
|
|
26
|
+
export declare function lockPath(cwd?: string): string;
|
|
27
|
+
export declare function writeLock(lock: UiLock): void;
|
|
28
|
+
export declare function clearLock(): void;
|
|
29
|
+
export declare function readLock(): UiLock | null;
|
|
30
|
+
/**
|
|
31
|
+
* Is the console named by the lock actually answering?
|
|
32
|
+
*
|
|
33
|
+
* Checked with a real request rather than by testing whether the pid exists: a
|
|
34
|
+
* pid can be reused, and what matters is not that *something* is alive but that
|
|
35
|
+
* it is a console that accepts this token. Short timeout — this runs before a
|
|
36
|
+
* human sees anything, so a stale lock must not cost them a visible pause.
|
|
37
|
+
*/
|
|
38
|
+
export declare function probe(lock: UiLock, timeoutMs?: number): Promise<boolean>;
|
|
39
|
+
/** The URL of a console already serving this project, or null. */
|
|
40
|
+
export declare function findRunningConsole(): Promise<string | null>;
|
package/dist/ui/lock.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.UI_LOCK_FILENAME = void 0;
|
|
37
|
+
exports.lockPath = lockPath;
|
|
38
|
+
exports.writeLock = writeLock;
|
|
39
|
+
exports.clearLock = clearLock;
|
|
40
|
+
exports.readLock = readLock;
|
|
41
|
+
exports.probe = probe;
|
|
42
|
+
exports.findRunningConsole = findRunningConsole;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const http = __importStar(require("http"));
|
|
45
|
+
const path = __importStar(require("path"));
|
|
46
|
+
const config_1 = require("../config");
|
|
47
|
+
/**
|
|
48
|
+
* Who is already serving this project's console — `.ia-qa/ui.lock`.
|
|
49
|
+
*
|
|
50
|
+
* The console is opened by clicking something, and people click twice. Without
|
|
51
|
+
* this, the second click either fails on a port collision or starts a *second*
|
|
52
|
+
* server with a *different* token, leaving an orphan process and a browser tab
|
|
53
|
+
* that will 401 the moment the first one is closed. Neither is explicable to
|
|
54
|
+
* the person who clicked.
|
|
55
|
+
*
|
|
56
|
+
* So the lock holds the port and the token, and a launcher that finds a live
|
|
57
|
+
* console simply opens it. The file is a hint, never an authority: it is
|
|
58
|
+
* verified by an actual request before being believed (a lock left behind by a
|
|
59
|
+
* killed process is the normal case, not the exception), and it lives next to
|
|
60
|
+
* the contracts because that is what it is scoped to — one console per project
|
|
61
|
+
* directory, not one per machine.
|
|
62
|
+
*
|
|
63
|
+
* It holds a session token, so it is a file to gitignore, like `_shots/`.
|
|
64
|
+
*/
|
|
65
|
+
exports.UI_LOCK_FILENAME = 'ui.lock';
|
|
66
|
+
function lockPath(cwd = (0, config_1.baseDir)()) {
|
|
67
|
+
return path.join(cwd, config_1.CONFIG_DIR, exports.UI_LOCK_FILENAME);
|
|
68
|
+
}
|
|
69
|
+
function writeLock(lock) {
|
|
70
|
+
try {
|
|
71
|
+
fs.mkdirSync(path.dirname(lockPath()), { recursive: true });
|
|
72
|
+
// 0600: it carries the session token, and the console it unlocks can edit
|
|
73
|
+
// test files. On Windows the mode is advisory, which is why the token is
|
|
74
|
+
// random per process and dies with it rather than being a stored secret.
|
|
75
|
+
fs.writeFileSync(lockPath(), JSON.stringify(lock), { encoding: 'utf8', mode: 0o600 });
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
/* a console that cannot write its lock still works; it just cannot be found */
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function clearLock() {
|
|
82
|
+
try {
|
|
83
|
+
fs.unlinkSync(lockPath());
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
/* already gone */
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function readLock() {
|
|
90
|
+
try {
|
|
91
|
+
const raw = JSON.parse(fs.readFileSync(lockPath(), 'utf8'));
|
|
92
|
+
if (!Number.isInteger(raw.port) || typeof raw.token !== 'string' || raw.token === '')
|
|
93
|
+
return null;
|
|
94
|
+
return raw;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Is the console named by the lock actually answering?
|
|
102
|
+
*
|
|
103
|
+
* Checked with a real request rather than by testing whether the pid exists: a
|
|
104
|
+
* pid can be reused, and what matters is not that *something* is alive but that
|
|
105
|
+
* it is a console that accepts this token. Short timeout — this runs before a
|
|
106
|
+
* human sees anything, so a stale lock must not cost them a visible pause.
|
|
107
|
+
*/
|
|
108
|
+
function probe(lock, timeoutMs = 700) {
|
|
109
|
+
return new Promise((resolve) => {
|
|
110
|
+
const req = http.get({
|
|
111
|
+
host: '127.0.0.1',
|
|
112
|
+
port: lock.port,
|
|
113
|
+
path: `/api/state?t=${encodeURIComponent(lock.token)}`,
|
|
114
|
+
timeout: timeoutMs,
|
|
115
|
+
headers: { Host: `127.0.0.1:${lock.port}` },
|
|
116
|
+
}, (res) => {
|
|
117
|
+
res.resume();
|
|
118
|
+
resolve(res.statusCode === 200);
|
|
119
|
+
});
|
|
120
|
+
req.on('timeout', () => {
|
|
121
|
+
req.destroy();
|
|
122
|
+
resolve(false);
|
|
123
|
+
});
|
|
124
|
+
req.on('error', () => resolve(false));
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
/** The URL of a console already serving this project, or null. */
|
|
128
|
+
async function findRunningConsole() {
|
|
129
|
+
const lock = readLock();
|
|
130
|
+
if (!lock)
|
|
131
|
+
return null;
|
|
132
|
+
if (!(await probe(lock))) {
|
|
133
|
+
clearLock();
|
|
134
|
+
return null;
|
|
135
|
+
}
|
|
136
|
+
return `http://127.0.0.1:${lock.port}/?t=${lock.token}`;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/ui/lock.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function renderConsoleHtml(token: string): string;
|