looply-cli 0.5.13 → 0.5.17
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/.src-hash +1 -1
- package/dist/bin.d.ts.map +1 -1
- package/dist/bin.js +8 -0
- package/dist/bin.js.map +1 -1
- package/dist/config.d.ts +19 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +29 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon-cmd.d.ts.map +1 -1
- package/dist/daemon-cmd.js +18 -0
- package/dist/daemon-cmd.js.map +1 -1
- package/dist/heroku-api.d.ts +87 -0
- package/dist/heroku-api.d.ts.map +1 -0
- package/dist/heroku-api.js +178 -0
- package/dist/heroku-api.js.map +1 -0
- package/dist/preview-cmd.d.ts +51 -0
- package/dist/preview-cmd.d.ts.map +1 -0
- package/dist/preview-cmd.js +194 -0
- package/dist/preview-cmd.js.map +1 -0
- package/dist/preview-environments.d.ts +173 -0
- package/dist/preview-environments.d.ts.map +1 -0
- package/dist/preview-environments.js +482 -0
- package/dist/preview-environments.js.map +1 -0
- package/dist/preview-github.d.ts +63 -0
- package/dist/preview-github.d.ts.map +1 -0
- package/dist/preview-github.js +130 -0
- package/dist/preview-github.js.map +1 -0
- package/dist/preview-settings-cache.d.ts +33 -0
- package/dist/preview-settings-cache.d.ts.map +1 -0
- package/dist/preview-settings-cache.js +108 -0
- package/dist/preview-settings-cache.js.map +1 -0
- package/dist/preview-wiring.d.ts +35 -0
- package/dist/preview-wiring.d.ts.map +1 -0
- package/dist/preview-wiring.js +81 -0
- package/dist/preview-wiring.js.map +1 -0
- package/dist/run-spend-report.d.ts +55 -0
- package/dist/run-spend-report.d.ts.map +1 -0
- package/dist/run-spend-report.js +84 -0
- package/dist/run-spend-report.js.map +1 -0
- package/dist/server-client.d.ts +22 -0
- package/dist/server-client.d.ts.map +1 -1
- package/dist/server-client.js +26 -0
- package/dist/server-client.js.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* WHAT A PROVIDER ALREADY SAID ABOUT A PULL REQUEST'S PREVIEW, READ FROM GITHUB.
|
|
4
|
+
*
|
|
5
|
+
* Netlify and Heroku both report their preview builds back to GitHub on the
|
|
6
|
+
* pull request's head commit — Netlify as a commit status
|
|
7
|
+
* (`netlify/<site>/deploy-preview`, its `target_url` the preview) and as a
|
|
8
|
+
* deployment; Heroku review apps as a deployment whose status carries the app's
|
|
9
|
+
* `environment_url`. Reading those means Looply needs no Netlify credentials at
|
|
10
|
+
* all and no Heroku ones to FIND a preview: it is the provider's own mechanism,
|
|
11
|
+
* observed.
|
|
12
|
+
*
|
|
13
|
+
* Read-only: GET only, with the token `github-api.ts` already uses for pull
|
|
14
|
+
* requests. One write-free exception, `sourceTarballUrl`, asks GitHub for the
|
|
15
|
+
* short-lived download link of a commit's source — a GET that is answered with a
|
|
16
|
+
* redirect — which Heroku needs to build a review app Looply asked for.
|
|
17
|
+
*/
|
|
18
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
+
exports.makeGitHubPreviewReader = makeGitHubPreviewReader;
|
|
20
|
+
exports.netlifySignal = netlifySignal;
|
|
21
|
+
exports.herokuSignal = herokuSignal;
|
|
22
|
+
function makeGitHubPreviewReader(deps) {
|
|
23
|
+
const doFetch = deps.fetch ?? ((url, init) => fetch(url, init));
|
|
24
|
+
const base = `https://api.github.com/repos/${encodeURIComponent(deps.owner)}/${encodeURIComponent(deps.repo)}`;
|
|
25
|
+
const headers = {
|
|
26
|
+
authorization: `Bearer ${deps.token}`,
|
|
27
|
+
accept: 'application/vnd.github+json',
|
|
28
|
+
'x-github-api-version': '2022-11-28',
|
|
29
|
+
'user-agent': 'looply-cli',
|
|
30
|
+
};
|
|
31
|
+
async function get(path) {
|
|
32
|
+
const res = await doFetch(`${base}${path}`, { method: 'GET', headers });
|
|
33
|
+
const text = await res.text();
|
|
34
|
+
if (res.status === 404)
|
|
35
|
+
return null;
|
|
36
|
+
if (res.status < 200 || res.status >= 300)
|
|
37
|
+
throw new Error(`GitHub answered ${res.status} to GET ${path}`);
|
|
38
|
+
return (text === '' ? null : JSON.parse(text));
|
|
39
|
+
}
|
|
40
|
+
const str = (x) => (typeof x === 'string' && x !== '' ? x : null);
|
|
41
|
+
return {
|
|
42
|
+
async signals(sha) {
|
|
43
|
+
if (!/^[0-9a-f]{7,64}$/.test(sha))
|
|
44
|
+
return [];
|
|
45
|
+
const out = [];
|
|
46
|
+
const seen = new Set();
|
|
47
|
+
const status = await get(`/commits/${sha}/status`);
|
|
48
|
+
for (const s of status?.statuses ?? []) {
|
|
49
|
+
const name = String(s['context'] ?? '');
|
|
50
|
+
if (name === '' || seen.has(`s:${name}`))
|
|
51
|
+
continue; // newest first: the first of each context is its state
|
|
52
|
+
seen.add(`s:${name}`);
|
|
53
|
+
const st = String(s['state']);
|
|
54
|
+
out.push({
|
|
55
|
+
source: 'status',
|
|
56
|
+
name,
|
|
57
|
+
state: st === 'success' ? 'live' : st === 'pending' ? 'building' : 'failed',
|
|
58
|
+
url: str(s['target_url']),
|
|
59
|
+
description: str(s['description']),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const deployments = await get(`/deployments?sha=${sha}&per_page=20`);
|
|
63
|
+
for (const d of deployments ?? []) {
|
|
64
|
+
const name = String(d['environment'] ?? '');
|
|
65
|
+
if (name === '' || seen.has(`d:${name}`))
|
|
66
|
+
continue;
|
|
67
|
+
seen.add(`d:${name}`);
|
|
68
|
+
const statuses = await get(`/deployments/${Number(d['id'])}/statuses?per_page=1`);
|
|
69
|
+
const latest = (statuses ?? [])[0];
|
|
70
|
+
const st = String(latest?.['state'] ?? 'pending');
|
|
71
|
+
out.push({
|
|
72
|
+
source: 'deployment',
|
|
73
|
+
name,
|
|
74
|
+
state: st === 'success' ? 'live' : st === 'inactive' ? 'inactive' : st === 'failure' || st === 'error' ? 'failed' : 'building',
|
|
75
|
+
url: str(latest?.['environment_url']) ?? str(latest?.['target_url']),
|
|
76
|
+
description: str(latest?.['description']),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
return out;
|
|
80
|
+
},
|
|
81
|
+
async sourceTarballUrl(sha) {
|
|
82
|
+
if (!/^[0-9a-f]{7,64}$/.test(sha))
|
|
83
|
+
return null;
|
|
84
|
+
const res = await doFetch(`${base}/tarball/${sha}`, { method: 'GET', headers, redirect: 'manual' });
|
|
85
|
+
const loc = res.headers?.get('location') ?? null;
|
|
86
|
+
return res.status >= 300 && res.status < 400 && loc !== null && loc.startsWith('https://') ? loc : null;
|
|
87
|
+
},
|
|
88
|
+
async openPulls(limit) {
|
|
89
|
+
const list = await get(`/pulls?state=open&per_page=${Math.max(1, Math.min(limit, 30))}&sort=updated&direction=desc`);
|
|
90
|
+
return (list ?? []).map((p) => ({
|
|
91
|
+
number: Number(p['number']),
|
|
92
|
+
url: String(p['html_url'] ?? ''),
|
|
93
|
+
branch: String(p['head']?.['ref'] ?? ''),
|
|
94
|
+
headSha: String(p['head']?.['sha'] ?? ''),
|
|
95
|
+
title: String(p['title'] ?? ''),
|
|
96
|
+
}));
|
|
97
|
+
},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/* ───────────────────────────────────────────────────── which signal is it ── */
|
|
101
|
+
/** Netlify's deploy-preview signal for this pull request, if any; `site` narrows it to one site. */
|
|
102
|
+
function netlifySignal(signals, site) {
|
|
103
|
+
const mine = (s) => {
|
|
104
|
+
const ctx = s.name.toLowerCase();
|
|
105
|
+
const url = (s.url ?? '').toLowerCase();
|
|
106
|
+
const isNetlify = (s.source === 'status' && /^netlify\/[^/]+\/deploy-preview$/.test(ctx)) ||
|
|
107
|
+
(s.source === 'deployment' && /deploy[- ]preview|netlify/.test(ctx)) ||
|
|
108
|
+
/\.netlify\.app(\/|$)/.test(url);
|
|
109
|
+
if (!isNetlify)
|
|
110
|
+
return false;
|
|
111
|
+
if (site === undefined)
|
|
112
|
+
return true;
|
|
113
|
+
return ctx === `netlify/${site.toLowerCase()}/deploy-preview` || url.includes(`--${site.toLowerCase()}.netlify.app`);
|
|
114
|
+
};
|
|
115
|
+
// A commit status names the site explicitly; prefer it over a deployment.
|
|
116
|
+
return signals.find((s) => s.source === 'status' && mine(s)) ?? signals.find(mine) ?? null;
|
|
117
|
+
}
|
|
118
|
+
/** Heroku's review-app deployment for pull request `n` of `pipeline`, if GitHub has one. */
|
|
119
|
+
function herokuSignal(signals, pipeline, n) {
|
|
120
|
+
const p = pipeline.toLowerCase();
|
|
121
|
+
const pr = new RegExp(`-pr-${n}(?![0-9])`);
|
|
122
|
+
return (signals.find((s) => {
|
|
123
|
+
if (s.source !== 'deployment')
|
|
124
|
+
return false;
|
|
125
|
+
const env = s.name.toLowerCase();
|
|
126
|
+
const url = (s.url ?? '').toLowerCase();
|
|
127
|
+
return (pr.test(env) || pr.test(url)) && (env.includes(p) || url.includes('herokuapp.com'));
|
|
128
|
+
}) ?? null);
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=preview-github.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-github.js","sourceRoot":"","sources":["../src/preview-github.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { type PreviewSettings } from '@looply/core';
|
|
2
|
+
/**
|
|
3
|
+
* THE PROJECT'S REMOTE PREVIEW CHOICE, AS THIS MACHINE LAST HEARD IT.
|
|
4
|
+
*
|
|
5
|
+
* Set in AutoRyn per component; pulled by the daemon with the delivery setting
|
|
6
|
+
* (every five minutes) and written here. A failed pull keeps the last answer.
|
|
7
|
+
*
|
|
8
|
+
* Precedence: config.yaml `preview.provider` → this cache → none.
|
|
9
|
+
*/
|
|
10
|
+
export declare const PREVIEW_SETTINGS_FILE = "preview-settings.json";
|
|
11
|
+
export interface PreviewSettingsCache {
|
|
12
|
+
settings: PreviewSettings | null;
|
|
13
|
+
updatedAt: string | null;
|
|
14
|
+
fetchedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function readPreviewSettingsCache(stateDir: string): PreviewSettingsCache | null;
|
|
17
|
+
/** The provider in force: config.yaml's, else the remote one, else null (none). */
|
|
18
|
+
export declare function resolvePreview(local: PreviewSettings | undefined, stateDir: string): {
|
|
19
|
+
settings: PreviewSettings | null;
|
|
20
|
+
from: 'config.yaml' | 'remote' | 'default';
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* One pull: ask the server, write the cache when the answer changed. Never
|
|
24
|
+
* throws. A server without the route (older looply-api) answers 404, which is
|
|
25
|
+
* "no preview", quietly.
|
|
26
|
+
*/
|
|
27
|
+
export declare function makePreviewSettingsPull(deps: {
|
|
28
|
+
fetch(): Promise<unknown>;
|
|
29
|
+
stateDir: string;
|
|
30
|
+
log(line: string): void;
|
|
31
|
+
now(): number;
|
|
32
|
+
}): () => Promise<'updated' | 'unchanged' | 'failed'>;
|
|
33
|
+
//# sourceMappingURL=preview-settings-cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-settings-cache.d.ts","sourceRoot":"","sources":["../src/preview-settings-cache.ts"],"names":[],"mappings":"AAEA,OAAO,EAA2B,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAG7E;;;;;;;GAOG;AAEH,eAAO,MAAM,qBAAqB,0BAA0B,CAAC;AAE7D,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IACjC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,GAAG,oBAAoB,GAAG,IAAI,CAiBtF;AAED,mFAAmF;AACnF,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,SAAS,EAAE,QAAQ,EAAE,MAAM,GAAG;IAAE,QAAQ,EAAE,eAAe,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,aAAa,GAAG,QAAQ,GAAG,SAAS,CAAA;CAAE,CAKrK;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE;IAC5C,KAAK,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,GAAG,IAAI,MAAM,CAAC;CACf,GAAG,MAAM,OAAO,CAAC,SAAS,GAAG,WAAW,GAAG,QAAQ,CAAC,CA+CpD"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PREVIEW_SETTINGS_FILE = void 0;
|
|
4
|
+
exports.readPreviewSettingsCache = readPreviewSettingsCache;
|
|
5
|
+
exports.resolvePreview = resolvePreview;
|
|
6
|
+
exports.makePreviewSettingsPull = makePreviewSettingsPull;
|
|
7
|
+
const node_fs_1 = require("node:fs");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const core_1 = require("@looply/core");
|
|
10
|
+
const server_client_1 = require("./server-client");
|
|
11
|
+
/**
|
|
12
|
+
* THE PROJECT'S REMOTE PREVIEW CHOICE, AS THIS MACHINE LAST HEARD IT.
|
|
13
|
+
*
|
|
14
|
+
* Set in AutoRyn per component; pulled by the daemon with the delivery setting
|
|
15
|
+
* (every five minutes) and written here. A failed pull keeps the last answer.
|
|
16
|
+
*
|
|
17
|
+
* Precedence: config.yaml `preview.provider` → this cache → none.
|
|
18
|
+
*/
|
|
19
|
+
exports.PREVIEW_SETTINGS_FILE = 'preview-settings.json';
|
|
20
|
+
function readPreviewSettingsCache(stateDir) {
|
|
21
|
+
try {
|
|
22
|
+
const raw = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(stateDir, exports.PREVIEW_SETTINGS_FILE), 'utf8'));
|
|
23
|
+
let settings = null;
|
|
24
|
+
if (raw['settings'] !== null && raw['settings'] !== undefined) {
|
|
25
|
+
const v = (0, core_1.validatePreviewSettings)(raw['settings']);
|
|
26
|
+
if (!v.ok)
|
|
27
|
+
return null;
|
|
28
|
+
settings = v.settings;
|
|
29
|
+
}
|
|
30
|
+
return {
|
|
31
|
+
settings,
|
|
32
|
+
updatedAt: typeof raw['updatedAt'] === 'string' ? raw['updatedAt'] : null,
|
|
33
|
+
fetchedAt: typeof raw['fetchedAt'] === 'string' ? raw['fetchedAt'] : '',
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** The provider in force: config.yaml's, else the remote one, else null (none). */
|
|
41
|
+
function resolvePreview(local, stateDir) {
|
|
42
|
+
if (local !== undefined)
|
|
43
|
+
return { settings: local.provider === 'none' ? null : local, from: 'config.yaml' };
|
|
44
|
+
const remote = readPreviewSettingsCache(stateDir)?.settings ?? null;
|
|
45
|
+
if (remote !== null && remote.provider !== 'none')
|
|
46
|
+
return { settings: remote, from: 'remote' };
|
|
47
|
+
return { settings: null, from: 'default' };
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* One pull: ask the server, write the cache when the answer changed. Never
|
|
51
|
+
* throws. A server without the route (older looply-api) answers 404, which is
|
|
52
|
+
* "no preview", quietly.
|
|
53
|
+
*/
|
|
54
|
+
function makePreviewSettingsPull(deps) {
|
|
55
|
+
let failureReported = false;
|
|
56
|
+
const reportOnce = (why) => {
|
|
57
|
+
if (failureReported)
|
|
58
|
+
return;
|
|
59
|
+
failureReported = true;
|
|
60
|
+
deps.log(`preview : could not refresh the project's preview setting — ${why}`);
|
|
61
|
+
};
|
|
62
|
+
return async () => {
|
|
63
|
+
let body;
|
|
64
|
+
try {
|
|
65
|
+
body = await deps.fetch();
|
|
66
|
+
}
|
|
67
|
+
catch (e) {
|
|
68
|
+
if (e instanceof server_client_1.ServerError && e.status === 404)
|
|
69
|
+
return 'failed';
|
|
70
|
+
if (!(e instanceof server_client_1.ServerError && (e.isOffline || e.isAuth)))
|
|
71
|
+
reportOnce(e.message);
|
|
72
|
+
return 'failed';
|
|
73
|
+
}
|
|
74
|
+
const b = body;
|
|
75
|
+
if (typeof b !== 'object' || b === null || !('settings' in b)) {
|
|
76
|
+
reportOnce('the server answered with something that is not a settings envelope');
|
|
77
|
+
return 'failed';
|
|
78
|
+
}
|
|
79
|
+
let settings = null;
|
|
80
|
+
if (b.settings !== null) {
|
|
81
|
+
const v = (0, core_1.validatePreviewSettings)(b.settings);
|
|
82
|
+
if (!v.ok) {
|
|
83
|
+
reportOnce(`the server sent a setting this build refuses (${v.key}: ${v.because})`);
|
|
84
|
+
return 'failed';
|
|
85
|
+
}
|
|
86
|
+
settings = v.settings;
|
|
87
|
+
}
|
|
88
|
+
failureReported = false;
|
|
89
|
+
const updatedAt = typeof b.updatedAt === 'string' ? b.updatedAt : null;
|
|
90
|
+
const before = readPreviewSettingsCache(deps.stateDir);
|
|
91
|
+
if (before !== null && JSON.stringify(before.settings) === JSON.stringify(settings) && before.updatedAt === updatedAt)
|
|
92
|
+
return 'unchanged';
|
|
93
|
+
try {
|
|
94
|
+
(0, node_fs_1.mkdirSync)(deps.stateDir, { recursive: true });
|
|
95
|
+
const target = (0, node_path_1.join)(deps.stateDir, exports.PREVIEW_SETTINGS_FILE);
|
|
96
|
+
const tmp = `${target}.${process.pid}.tmp`;
|
|
97
|
+
(0, node_fs_1.writeFileSync)(tmp, JSON.stringify({ settings, updatedAt, fetchedAt: new Date(deps.now()).toISOString() }, null, 2) + '\n', 'utf8');
|
|
98
|
+
(0, node_fs_1.renameSync)(tmp, target);
|
|
99
|
+
}
|
|
100
|
+
catch (e) {
|
|
101
|
+
deps.log(`preview : could not write the preview setting cache (${e.message})`);
|
|
102
|
+
return 'failed';
|
|
103
|
+
}
|
|
104
|
+
deps.log(`preview : project preview setting is ${settings === null ? 'none' : settings.provider}`);
|
|
105
|
+
return 'updated';
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=preview-settings-cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-settings-cache.js","sourceRoot":"","sources":["../src/preview-settings-cache.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { LooplyConfig } from './config';
|
|
2
|
+
import { type GitHubApi } from './github-api';
|
|
3
|
+
import { type HerokuApi } from './heroku-api';
|
|
4
|
+
import { type PreviewCommandRunner } from './preview-environments';
|
|
5
|
+
import { type GitHubPreviewReader } from './preview-github';
|
|
6
|
+
import type { ServerClient } from './server-client';
|
|
7
|
+
/**
|
|
8
|
+
* THE DAEMON'S HANDLE ON PREVIEW SITES: built once per project, so `daemon-cmd`
|
|
9
|
+
* gains one call on its five-minute cadence and nothing else.
|
|
10
|
+
*
|
|
11
|
+
* tick() — pull the project's preview setting, then one pass over delivered
|
|
12
|
+
* pull requests: read or start each open one's preview, tear down
|
|
13
|
+
* each closed one's.
|
|
14
|
+
*/
|
|
15
|
+
export interface PreviewAdvancer {
|
|
16
|
+
tick(): Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
export interface PreviewWiringOpts {
|
|
19
|
+
cwd: string;
|
|
20
|
+
stateDir: string;
|
|
21
|
+
config: Pick<LooplyConfig, 'preview'>;
|
|
22
|
+
client: Pick<ServerClient, 'recordPreview' | 'projectPreviewSettings'>;
|
|
23
|
+
projectId: string;
|
|
24
|
+
log(line: string): void;
|
|
25
|
+
/** Tests: fakes for every outside party. */
|
|
26
|
+
github?: (owner: string, repo: string) => Promise<{
|
|
27
|
+
api: GitHubApi;
|
|
28
|
+
reader: GitHubPreviewReader;
|
|
29
|
+
} | null>;
|
|
30
|
+
heroku?: () => HerokuApi | null;
|
|
31
|
+
runCommand?: PreviewCommandRunner;
|
|
32
|
+
now?: () => number;
|
|
33
|
+
}
|
|
34
|
+
export declare function makePreviewAdvancer(opts: PreviewWiringOpts): PreviewAdvancer;
|
|
35
|
+
//# sourceMappingURL=preview-wiring.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-wiring.d.ts","sourceRoot":"","sources":["../src/preview-wiring.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAC7C,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,cAAc,CAAC;AAC1E,OAAO,EASL,KAAK,oBAAoB,EAC1B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAA2B,KAAK,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAErF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAEpD;;;;;;;GAOG;AACH,MAAM,WAAW,eAAe;IAC9B,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAC;IACtC,MAAM,EAAE,IAAI,CAAC,YAAY,EAAE,eAAe,GAAG,wBAAwB,CAAC,CAAC;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,4CAA4C;IAC5C,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,GAAG,EAAE,SAAS,CAAC;QAAC,MAAM,EAAE,mBAAmB,CAAA;KAAE,GAAG,IAAI,CAAC,CAAC;IAC1G,MAAM,CAAC,EAAE,MAAM,SAAS,GAAG,IAAI,CAAC;IAChC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAClC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACpB;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,iBAAiB,GAAG,eAAe,CAkE5E"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.makePreviewAdvancer = makePreviewAdvancer;
|
|
4
|
+
const branch_delivery_1 = require("./branch-delivery");
|
|
5
|
+
const github_api_1 = require("./github-api");
|
|
6
|
+
const heroku_api_1 = require("./heroku-api");
|
|
7
|
+
const preview_environments_1 = require("./preview-environments");
|
|
8
|
+
const preview_github_1 = require("./preview-github");
|
|
9
|
+
const preview_settings_cache_1 = require("./preview-settings-cache");
|
|
10
|
+
function makePreviewAdvancer(opts) {
|
|
11
|
+
const now = opts.now ?? (() => Date.now());
|
|
12
|
+
const pull = (0, preview_settings_cache_1.makePreviewSettingsPull)({
|
|
13
|
+
fetch: () => opts.client.projectPreviewSettings(opts.projectId),
|
|
14
|
+
stateDir: opts.stateDir,
|
|
15
|
+
log: opts.log,
|
|
16
|
+
now,
|
|
17
|
+
});
|
|
18
|
+
const github = opts.github ??
|
|
19
|
+
(async (owner, repo) => {
|
|
20
|
+
const t = await (0, github_api_1.githubToken)();
|
|
21
|
+
if (t === null)
|
|
22
|
+
return null;
|
|
23
|
+
return { api: (0, github_api_1.makeGitHubApi)({ owner, repo, token: t.token }), reader: (0, preview_github_1.makeGitHubPreviewReader)({ owner, repo, token: t.token }) };
|
|
24
|
+
});
|
|
25
|
+
const heroku = opts.heroku ??
|
|
26
|
+
(() => {
|
|
27
|
+
const t = (0, heroku_api_1.herokuToken)();
|
|
28
|
+
return t === null ? null : (0, heroku_api_1.makeHerokuApi)({ token: t.token });
|
|
29
|
+
});
|
|
30
|
+
const run = opts.runCommand ?? (0, preview_environments_1.makePreviewCommandRunner)(opts.cwd, opts.stateDir);
|
|
31
|
+
const settings = () => (0, preview_settings_cache_1.resolvePreview)(opts.config.preview?.settings, opts.stateDir).settings;
|
|
32
|
+
/** One command driver for the life of the daemon: it holds the background builds. */
|
|
33
|
+
const command = (0, preview_environments_1.commandDriver)({ ...(opts.config.preview?.command ? { command: opts.config.preview.command } : {}), ...(opts.config.preview?.teardown ? { teardown: opts.config.preview.teardown } : {}) }, run);
|
|
34
|
+
const store = (0, preview_environments_1.filePreviewStore)(opts.stateDir);
|
|
35
|
+
const deliveries = (0, branch_delivery_1.fileDeliveryStore)(opts.stateDir);
|
|
36
|
+
return {
|
|
37
|
+
async tick() {
|
|
38
|
+
await pull();
|
|
39
|
+
// A reader per repository per pass; the drivers read from it.
|
|
40
|
+
const clients = new Map();
|
|
41
|
+
const clientFor = async (owner, repo) => {
|
|
42
|
+
const k = `${owner}/${repo}`;
|
|
43
|
+
if (!clients.has(k))
|
|
44
|
+
clients.set(k, await github(owner, repo).catch(() => null));
|
|
45
|
+
return clients.get(k);
|
|
46
|
+
};
|
|
47
|
+
let current = null;
|
|
48
|
+
const herokuApi = heroku();
|
|
49
|
+
const driver = (provider) => {
|
|
50
|
+
if (current === null)
|
|
51
|
+
return null;
|
|
52
|
+
const s = settings();
|
|
53
|
+
if (provider === 'command')
|
|
54
|
+
return command;
|
|
55
|
+
if (provider === 'netlify')
|
|
56
|
+
return (0, preview_environments_1.netlifyDriver)(s?.provider === 'netlify' ? s : {}, current.reader);
|
|
57
|
+
// An existing heroku preview whose setting moved away is still torn down on its pipeline name when known.
|
|
58
|
+
if (s?.provider === 'heroku')
|
|
59
|
+
return (0, preview_environments_1.herokuDriver)(s, current.reader, herokuApi);
|
|
60
|
+
return null;
|
|
61
|
+
};
|
|
62
|
+
await (0, preview_environments_1.advancePreviews)({
|
|
63
|
+
settings,
|
|
64
|
+
driver,
|
|
65
|
+
deliveries,
|
|
66
|
+
store,
|
|
67
|
+
github: async (owner, repo) => {
|
|
68
|
+
current = await clientFor(owner, repo);
|
|
69
|
+
return current?.api ?? null;
|
|
70
|
+
},
|
|
71
|
+
record: async (goalId, report) => {
|
|
72
|
+
if (goalId.startsWith('gol_'))
|
|
73
|
+
await opts.client.recordPreview(opts.projectId, goalId, report);
|
|
74
|
+
},
|
|
75
|
+
now,
|
|
76
|
+
log: opts.log,
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=preview-wiring.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-wiring.js","sourceRoot":"","sources":["../src/preview-wiring.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type { SpendRecord } from './spend-ledger';
|
|
2
|
+
/**
|
|
3
|
+
* WHAT EACH RUN COST, SENT TO THE SERVER SO A DASHBOARD CAN SAY "COST PER TICKET".
|
|
4
|
+
*
|
|
5
|
+
* The spend ledger (`~/.looply/spend.jsonl`) is machine-local and append-only;
|
|
6
|
+
* nothing else ever left the machine. This totals its rows per run and hands the
|
|
7
|
+
* server the totals — the server already knows which goal each run served, so it
|
|
8
|
+
* can answer "what did this ticket cost" without the machine knowing about tickets.
|
|
9
|
+
*
|
|
10
|
+
* ⚠️ ONLY `spend` ROWS ARE SUMMED. A reservation is an estimate, and a settlement
|
|
11
|
+
* restates rows that are already here; adding either would count money twice.
|
|
12
|
+
*
|
|
13
|
+
* ⚠️ AN INVOCATION WITH NO DOLLARS IS COUNTED AS UNMEASURED, NEVER AS FREE. The
|
|
14
|
+
* dashboard shows the measured dollars and says how many calls it could not price.
|
|
15
|
+
*
|
|
16
|
+
* Read-only on the ledger, bounded per call, and a failure only waits for the
|
|
17
|
+
* next cadence.
|
|
18
|
+
*/
|
|
19
|
+
export interface RunSpendSummary {
|
|
20
|
+
readonly runId: string;
|
|
21
|
+
/** Model invocations recorded for the run. */
|
|
22
|
+
readonly invocations: number;
|
|
23
|
+
/** Of those, how many reported no dollar figure. */
|
|
24
|
+
readonly unmeasured: number;
|
|
25
|
+
/** Dollars of the invocations that reported one. */
|
|
26
|
+
readonly usd: number;
|
|
27
|
+
readonly inputTokens: number;
|
|
28
|
+
readonly outputTokens: number;
|
|
29
|
+
readonly cacheReadTokens: number;
|
|
30
|
+
readonly cacheWriteTokens: number;
|
|
31
|
+
/** Wall clock spent inside model invocations. */
|
|
32
|
+
readonly ms: number;
|
|
33
|
+
/** ISO time of the run's latest row. */
|
|
34
|
+
readonly lastAt: string;
|
|
35
|
+
}
|
|
36
|
+
/** Runs older than this are not reported (the dashboard looks back 90 days). */
|
|
37
|
+
export declare const RUN_SPEND_LOOKBACK_MS: number;
|
|
38
|
+
/** At most this many runs per request; the rest go on the next cadence. */
|
|
39
|
+
export declare const RUN_SPEND_BATCH = 200;
|
|
40
|
+
/** Totals per run, newest run first, for runs whose latest row is after `sinceMs`. */
|
|
41
|
+
export declare function summariseRunSpend(records: readonly SpendRecord[], sinceMs: number): RunSpendSummary[];
|
|
42
|
+
export interface RunSpendReporterDeps {
|
|
43
|
+
/** The ledger's rows; `null` when it cannot be read (nothing is sent). */
|
|
44
|
+
readonly read: () => readonly SpendRecord[] | null;
|
|
45
|
+
/** Sends one batch; resolves when the server took it. */
|
|
46
|
+
readonly send: (runs: readonly RunSpendSummary[]) => Promise<unknown>;
|
|
47
|
+
readonly now?: () => number;
|
|
48
|
+
readonly log?: (line: string) => void;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* One reporting pass per call. Sends only runs whose totals changed since the
|
|
52
|
+
* last pass that the server accepted, so a quiet machine sends nothing.
|
|
53
|
+
*/
|
|
54
|
+
export declare function makeRunSpendReporter(deps: RunSpendReporterDeps): () => Promise<number>;
|
|
55
|
+
//# sourceMappingURL=run-spend-report.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-spend-report.d.ts","sourceRoot":"","sources":["../src/run-spend-report.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,8CAA8C;IAC9C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,oDAAoD;IACpD,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,iDAAiD;IACjD,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,wCAAwC;IACxC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;CACzB;AAED,gFAAgF;AAChF,eAAO,MAAM,qBAAqB,QAA2B,CAAC;AAC9D,2EAA2E;AAC3E,eAAO,MAAM,eAAe,MAAM,CAAC;AAInC,sFAAsF;AACtF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,SAAS,WAAW,EAAE,EAAE,OAAO,EAAE,MAAM,GAAG,eAAe,EAAE,CAmCrG;AAED,MAAM,WAAW,oBAAoB;IACnC,0EAA0E;IAC1E,QAAQ,CAAC,IAAI,EAAE,MAAM,SAAS,WAAW,EAAE,GAAG,IAAI,CAAC;IACnD,yDAAyD;IACzD,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,oBAAoB,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,CAuBtF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RUN_SPEND_BATCH = exports.RUN_SPEND_LOOKBACK_MS = void 0;
|
|
4
|
+
exports.summariseRunSpend = summariseRunSpend;
|
|
5
|
+
exports.makeRunSpendReporter = makeRunSpendReporter;
|
|
6
|
+
/** Runs older than this are not reported (the dashboard looks back 90 days). */
|
|
7
|
+
exports.RUN_SPEND_LOOKBACK_MS = 90 * 24 * 60 * 60 * 1000;
|
|
8
|
+
/** At most this many runs per request; the rest go on the next cadence. */
|
|
9
|
+
exports.RUN_SPEND_BATCH = 200;
|
|
10
|
+
const n = (v) => (typeof v === 'number' && Number.isFinite(v) && v >= 0 ? v : 0);
|
|
11
|
+
/** Totals per run, newest run first, for runs whose latest row is after `sinceMs`. */
|
|
12
|
+
function summariseRunSpend(records, sinceMs) {
|
|
13
|
+
const byRun = new Map();
|
|
14
|
+
for (const r of records) {
|
|
15
|
+
if (typeof r.runId !== 'string' || r.runId === '')
|
|
16
|
+
continue;
|
|
17
|
+
if (r.kind !== undefined && r.kind !== 'spend')
|
|
18
|
+
continue;
|
|
19
|
+
let s = byRun.get(r.runId);
|
|
20
|
+
if (!s) {
|
|
21
|
+
s = {
|
|
22
|
+
runId: r.runId,
|
|
23
|
+
invocations: 0,
|
|
24
|
+
unmeasured: 0,
|
|
25
|
+
usd: 0,
|
|
26
|
+
inputTokens: 0,
|
|
27
|
+
outputTokens: 0,
|
|
28
|
+
cacheReadTokens: 0,
|
|
29
|
+
cacheWriteTokens: 0,
|
|
30
|
+
ms: 0,
|
|
31
|
+
lastAt: r.at,
|
|
32
|
+
};
|
|
33
|
+
byRun.set(r.runId, s);
|
|
34
|
+
}
|
|
35
|
+
s.invocations += 1;
|
|
36
|
+
if (typeof r.usd === 'number' && Number.isFinite(r.usd))
|
|
37
|
+
s.usd += r.usd;
|
|
38
|
+
else
|
|
39
|
+
s.unmeasured += 1;
|
|
40
|
+
s.inputTokens += n(r.inputTokens);
|
|
41
|
+
s.outputTokens += n(r.outputTokens);
|
|
42
|
+
s.cacheReadTokens += n(r.cacheReadTokens);
|
|
43
|
+
s.cacheWriteTokens += n(r.cacheWriteTokens);
|
|
44
|
+
s.ms += n(r.ms);
|
|
45
|
+
if (Date.parse(r.at) > Date.parse(s.lastAt))
|
|
46
|
+
s.lastAt = r.at;
|
|
47
|
+
}
|
|
48
|
+
return [...byRun.values()]
|
|
49
|
+
.filter((s) => Date.parse(s.lastAt) >= sinceMs)
|
|
50
|
+
.map((s) => ({ ...s, usd: Math.round(s.usd * 1e6) / 1e6 }))
|
|
51
|
+
.sort((a, b) => Date.parse(b.lastAt) - Date.parse(a.lastAt));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* One reporting pass per call. Sends only runs whose totals changed since the
|
|
55
|
+
* last pass that the server accepted, so a quiet machine sends nothing.
|
|
56
|
+
*/
|
|
57
|
+
function makeRunSpendReporter(deps) {
|
|
58
|
+
const sent = new Map();
|
|
59
|
+
const now = deps.now ?? (() => Date.now());
|
|
60
|
+
let failing = false;
|
|
61
|
+
return async () => {
|
|
62
|
+
const records = deps.read();
|
|
63
|
+
if (records === null)
|
|
64
|
+
return 0;
|
|
65
|
+
const changed = summariseRunSpend(records, now() - exports.RUN_SPEND_LOOKBACK_MS).filter((s) => sent.get(s.runId) !== `${s.invocations}|${s.lastAt}`);
|
|
66
|
+
const batch = changed.slice(0, exports.RUN_SPEND_BATCH);
|
|
67
|
+
if (batch.length === 0)
|
|
68
|
+
return 0;
|
|
69
|
+
try {
|
|
70
|
+
await deps.send(batch);
|
|
71
|
+
}
|
|
72
|
+
catch (e) {
|
|
73
|
+
if (!failing)
|
|
74
|
+
deps.log?.(`spend : could not report run costs (${e.message}); will retry`);
|
|
75
|
+
failing = true;
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
failing = false;
|
|
79
|
+
for (const s of batch)
|
|
80
|
+
sent.set(s.runId, `${s.invocations}|${s.lastAt}`);
|
|
81
|
+
return batch.length;
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=run-spend-report.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run-spend-report.js","sourceRoot":"","sources":["../src/run-spend-report.ts"],"names":[],"mappings":"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"}
|
package/dist/server-client.d.ts
CHANGED
|
@@ -3,6 +3,8 @@ import type { Attestation, Classification, CoverageKind, Disposition, Requiremen
|
|
|
3
3
|
import type { VisualProofUpload } from '@looply/core';
|
|
4
4
|
import type { AgentSettings, MachineSettings, WorkspacesReport, AccountReport, MachineSlots, BranchDeliveryReport, DeliverySettings, AnyEvent, Goal, GoalAutonomyState, GoalState, MachineSettlableState, PursuitEndCode, GoalHold } from '@looply/core';
|
|
5
5
|
import type { IngestAck } from './uploader';
|
|
6
|
+
import type { PreviewReport, PreviewSettings } from '@looply/core';
|
|
7
|
+
import type { RunSpendSummary } from './run-spend-report';
|
|
6
8
|
/**
|
|
7
9
|
* The machine's client for the looply server.
|
|
8
10
|
*
|
|
@@ -113,6 +115,14 @@ export declare class ServerClient {
|
|
|
113
115
|
* (`GET /v1/machine/settings`; the id comes from the token). Cached by the
|
|
114
116
|
* daemon — see `machine-settings.ts`.
|
|
115
117
|
*/
|
|
118
|
+
/**
|
|
119
|
+
* What each of this project's runs cost, totalled from the machine's spend
|
|
120
|
+
* ledger (`run-spend-report.ts`). The server keeps the totals on the run rows it
|
|
121
|
+
* already has, and ignores run ids that are not this project's on this machine.
|
|
122
|
+
*/
|
|
123
|
+
reportRunSpend(machineId: string, projectId: string, runs: readonly RunSpendSummary[]): Promise<{
|
|
124
|
+
updated: number;
|
|
125
|
+
}>;
|
|
116
126
|
machineSettings(): Promise<{
|
|
117
127
|
settings: MachineSettings;
|
|
118
128
|
updatedAt: string | null;
|
|
@@ -268,6 +278,18 @@ export declare class ServerClient {
|
|
|
268
278
|
}>;
|
|
269
279
|
at: string;
|
|
270
280
|
}>;
|
|
281
|
+
/**
|
|
282
|
+
* The project's remote preview choice (`GET /v1/projects/:id/preview-settings`).
|
|
283
|
+
* The daemon caches the answer — see `preview-settings-cache.ts`.
|
|
284
|
+
*/
|
|
285
|
+
projectPreviewSettings(projectId: string): Promise<{
|
|
286
|
+
settings: PreviewSettings | null;
|
|
287
|
+
updatedAt: string | null;
|
|
288
|
+
}>;
|
|
289
|
+
/** Where a delivered goal's preview site stands — building, live, failed, removed (machine token). */
|
|
290
|
+
recordPreview(projectId: string, goalId: string, report: PreviewReport): Promise<{
|
|
291
|
+
preview: PreviewReport;
|
|
292
|
+
}>;
|
|
271
293
|
projectGoals(projectId: string): Promise<{
|
|
272
294
|
goals: Goal[];
|
|
273
295
|
}>;
|