@hover-dev/core 0.25.0 → 0.26.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cloud.d.ts +56 -0
- package/dist/cloud.d.ts.map +1 -0
- package/dist/cloud.js +93 -0
- package/package.json +5 -1
package/dist/cloud.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { RunFailure } from './specs/runFailures.js';
|
|
2
|
+
export declare const DEFAULT_CLOUD_URL = "https://cloud.gethover.dev";
|
|
3
|
+
export interface CloudCredentials {
|
|
4
|
+
/** Personal access token minted at cloud Settings → Access tokens. */
|
|
5
|
+
token: string;
|
|
6
|
+
/** Cloud base URL; DEFAULT_CLOUD_URL unless self-configured. */
|
|
7
|
+
url: string;
|
|
8
|
+
}
|
|
9
|
+
/** One drifted spec queued by the cloud, as /api/v1/heal-requests returns it.
|
|
10
|
+
* `hint` is RunFailure-shaped — feed it straight to the local heal flow. */
|
|
11
|
+
export interface CloudHealRequest {
|
|
12
|
+
id: string;
|
|
13
|
+
status: 'open' | 'routed' | 'healed' | 'dismissed';
|
|
14
|
+
specFile: string;
|
|
15
|
+
hint: RunFailure;
|
|
16
|
+
createdAt: string;
|
|
17
|
+
project: {
|
|
18
|
+
id: string;
|
|
19
|
+
name: string;
|
|
20
|
+
repo: string;
|
|
21
|
+
};
|
|
22
|
+
run: {
|
|
23
|
+
id: string;
|
|
24
|
+
branch: string | null;
|
|
25
|
+
commitSha: string | null;
|
|
26
|
+
ciUrl: string | null;
|
|
27
|
+
createdAt: string;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare function credentialsPath(home?: string): string;
|
|
31
|
+
/** Resolve credentials: env first, then ~/.hover/credentials.json. Null when
|
|
32
|
+
* neither is present (the surfaces show their "connect" hint on null). */
|
|
33
|
+
export declare function readCloudCredentials(env?: NodeJS.ProcessEnv, home?: string): CloudCredentials | null;
|
|
34
|
+
/** Persist credentials for every local surface (0600 — token, not a secret to
|
|
35
|
+
* sync; same trust model as ~/.aws/credentials). Returns the path written. */
|
|
36
|
+
export declare function writeCloudCredentials(creds: {
|
|
37
|
+
token: string;
|
|
38
|
+
url?: string;
|
|
39
|
+
}, home?: string): string;
|
|
40
|
+
/** Raised on a non-2xx cloud response; `status` 401 → token revoked/expired. */
|
|
41
|
+
export declare class CloudApiError extends Error {
|
|
42
|
+
status: number;
|
|
43
|
+
constructor(status: number, message: string);
|
|
44
|
+
}
|
|
45
|
+
/** The open heal queue (optionally one repo's slice, `owner/name`). */
|
|
46
|
+
export declare function fetchHealRequests(creds: CloudCredentials, opts?: {
|
|
47
|
+
status?: 'open' | 'routed' | 'healed' | 'dismissed' | 'all';
|
|
48
|
+
repo?: string;
|
|
49
|
+
}, fetchImpl?: typeof fetch): Promise<CloudHealRequest[]>;
|
|
50
|
+
/** Mark a request routed (a local heal picked it up) or dismissed / reopened.
|
|
51
|
+
* `healed` is not writable — only CI seeing the spec pass closes a request. */
|
|
52
|
+
export declare function updateHealRequest(creds: CloudCredentials, id: string, status: 'routed' | 'dismissed' | 'open', fetchImpl?: typeof fetch): Promise<void>;
|
|
53
|
+
/** The heal slug for a queued request (`checkout.spec.ts` → `checkout`) — what
|
|
54
|
+
* `/mcp__hover__heal <slug>` takes. */
|
|
55
|
+
export declare function healSlug(specFile: string): string;
|
|
56
|
+
//# sourceMappingURL=cloud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cloud.d.ts","sourceRoot":"","sources":["../src/cloud.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,eAAO,MAAM,iBAAiB,+BAA+B,CAAC;AAE9D,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,GAAG,EAAE,MAAM,CAAC;CACb;AAED;6EAC6E;AAC7E,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;IACnD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IACpD,GAAG,EAAE;QACH,EAAE,EAAE,MAAM,CAAC;QACX,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QACtB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;QACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;QACrB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;CACH;AAED,wBAAgB,eAAe,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEhE;AAED;2EAC2E;AAC3E,wBAAgB,oBAAoB,CAClC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,IAAI,CAAC,EAAE,MAAM,GACZ,gBAAgB,GAAG,IAAI,CAczB;AAED;+EAC+E;AAC/E,wBAAgB,qBAAqB,CACnC,KAAK,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,EACtC,IAAI,GAAE,MAAkB,GACvB,MAAM,CAOR;AAED,gFAAgF;AAChF,qBAAa,aAAc,SAAQ,KAAK;IAE7B,MAAM,EAAE,MAAM;gBAAd,MAAM,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM;CAIlB;AAuBD,uEAAuE;AACvE,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,gBAAgB,EACvB,IAAI,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,GAAG,KAAK,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAO,EACzF,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAY7B;AAED;gFACgF;AAChF,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,gBAAgB,EACvB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,QAAQ,GAAG,WAAW,GAAG,MAAM,EACvC,SAAS,GAAE,OAAO,KAAa,GAC9B,OAAO,CAAC,IAAI,CAAC,CAOf;AAED;wCACwC;AACxC,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGjD"}
|
package/dist/cloud.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hover Cloud client — the PULL side of the cloud ↔ editor loop.
|
|
3
|
+
*
|
|
4
|
+
* Cloud (cloud.gethover.dev) ingests CI runs and queues a heal request per
|
|
5
|
+
* drifted spec. Nothing in the cloud can reach into an editor: this module is
|
|
6
|
+
* how the local surfaces (the VS Code extension, the MCP server) pull that
|
|
7
|
+
* queue and feed it to the existing local heal flow. The fix stays local +
|
|
8
|
+
* human-reviewed; a request auto-closes only when CI sees the spec pass again.
|
|
9
|
+
*
|
|
10
|
+
* Credentials resolve in a fixed chain so one sign-in covers every surface:
|
|
11
|
+
* 1. HOVER_CLOUD_TOKEN (+ optional HOVER_CLOUD_URL) env vars — explicit, CI
|
|
12
|
+
* 2. ~/.hover/credentials.json — written by "Hover: Connect Hover Cloud" in
|
|
13
|
+
* VS Code (or by hand); 0600, shared by the extension AND the MCP server
|
|
14
|
+
* (VS Code SecretStorage is extension-private, so it can't be the store).
|
|
15
|
+
*/
|
|
16
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
17
|
+
import { homedir } from 'node:os';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
export const DEFAULT_CLOUD_URL = 'https://cloud.gethover.dev';
|
|
20
|
+
export function credentialsPath(home = homedir()) {
|
|
21
|
+
return join(home, '.hover', 'credentials.json');
|
|
22
|
+
}
|
|
23
|
+
/** Resolve credentials: env first, then ~/.hover/credentials.json. Null when
|
|
24
|
+
* neither is present (the surfaces show their "connect" hint on null). */
|
|
25
|
+
export function readCloudCredentials(env = process.env, home) {
|
|
26
|
+
if (env.HOVER_CLOUD_TOKEN) {
|
|
27
|
+
return { token: env.HOVER_CLOUD_TOKEN, url: env.HOVER_CLOUD_URL || DEFAULT_CLOUD_URL };
|
|
28
|
+
}
|
|
29
|
+
try {
|
|
30
|
+
const raw = JSON.parse(readFileSync(credentialsPath(home), 'utf8'));
|
|
31
|
+
if (!raw.token)
|
|
32
|
+
return null;
|
|
33
|
+
return { token: raw.token, url: raw.url || DEFAULT_CLOUD_URL };
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/** Persist credentials for every local surface (0600 — token, not a secret to
|
|
40
|
+
* sync; same trust model as ~/.aws/credentials). Returns the path written. */
|
|
41
|
+
export function writeCloudCredentials(creds, home = homedir()) {
|
|
42
|
+
const p = credentialsPath(home);
|
|
43
|
+
mkdirSync(join(home, '.hover'), { recursive: true });
|
|
44
|
+
writeFileSync(p, `${JSON.stringify({ url: DEFAULT_CLOUD_URL, ...creds }, null, 2)}\n`, {
|
|
45
|
+
mode: 0o600,
|
|
46
|
+
});
|
|
47
|
+
return p;
|
|
48
|
+
}
|
|
49
|
+
/** Raised on a non-2xx cloud response; `status` 401 → token revoked/expired. */
|
|
50
|
+
export class CloudApiError extends Error {
|
|
51
|
+
status;
|
|
52
|
+
constructor(status, message) {
|
|
53
|
+
super(message);
|
|
54
|
+
this.status = status;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
async function cloudJson(creds, path, init = {}, fetchImpl = fetch) {
|
|
58
|
+
const res = await fetchImpl(`${creds.url.replace(/\/$/, '')}${path}`, {
|
|
59
|
+
...init,
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${creds.token}`,
|
|
62
|
+
'Content-Type': 'application/json',
|
|
63
|
+
...init.headers,
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
if (!res.ok) {
|
|
67
|
+
const body = await res.text().catch(() => '');
|
|
68
|
+
throw new CloudApiError(res.status, `${path} → ${res.status}: ${body.slice(0, 200)}`);
|
|
69
|
+
}
|
|
70
|
+
return (await res.json());
|
|
71
|
+
}
|
|
72
|
+
/** The open heal queue (optionally one repo's slice, `owner/name`). */
|
|
73
|
+
export async function fetchHealRequests(creds, opts = {}, fetchImpl = fetch) {
|
|
74
|
+
const params = new URLSearchParams();
|
|
75
|
+
if (opts.status)
|
|
76
|
+
params.set('status', opts.status);
|
|
77
|
+
if (opts.repo)
|
|
78
|
+
params.set('repo', opts.repo);
|
|
79
|
+
const qs = params.size > 0 ? `?${params}` : '';
|
|
80
|
+
const data = await cloudJson(creds, `/api/v1/heal-requests${qs}`, {}, fetchImpl);
|
|
81
|
+
return data.healRequests;
|
|
82
|
+
}
|
|
83
|
+
/** Mark a request routed (a local heal picked it up) or dismissed / reopened.
|
|
84
|
+
* `healed` is not writable — only CI seeing the spec pass closes a request. */
|
|
85
|
+
export async function updateHealRequest(creds, id, status, fetchImpl = fetch) {
|
|
86
|
+
await cloudJson(creds, `/api/v1/heal-requests/${id}`, { method: 'PATCH', body: JSON.stringify({ status }) }, fetchImpl);
|
|
87
|
+
}
|
|
88
|
+
/** The heal slug for a queued request (`checkout.spec.ts` → `checkout`) — what
|
|
89
|
+
* `/mcp__hover__heal <slug>` takes. */
|
|
90
|
+
export function healSlug(specFile) {
|
|
91
|
+
const base = specFile.split('/').pop() ?? specFile;
|
|
92
|
+
return base.replace(/\.spec\.ts$/, '');
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hover-dev/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.0",
|
|
4
4
|
"description": "Hover's local Node service: agent invocation, Playwright CDP preflight, WebSocket bridge.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Hyperyond",
|
|
@@ -34,6 +34,10 @@
|
|
|
34
34
|
"./wiki": {
|
|
35
35
|
"types": "./dist/wiki.d.ts",
|
|
36
36
|
"import": "./dist/wiki.js"
|
|
37
|
+
},
|
|
38
|
+
"./cloud": {
|
|
39
|
+
"types": "./dist/cloud.d.ts",
|
|
40
|
+
"import": "./dist/cloud.js"
|
|
37
41
|
}
|
|
38
42
|
},
|
|
39
43
|
"files": [
|