@illuminis/comprism 0.1.2 → 0.1.4
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 +98 -36
- package/out/agent/command.d.ts +75 -4
- package/out/agent/command.js +220 -25
- package/out/agent/render.d.ts +17 -2
- package/out/agent/render.js +157 -14
- package/out/agent/session.d.ts +141 -2
- package/out/agent/session.js +735 -149
- package/out/commands/agents.d.ts +2 -0
- package/out/commands/agents.js +79 -0
- package/out/commands/ask.d.ts +1 -1
- package/out/commands/ask.js +78 -11
- package/out/commands/commands-thin.js +79 -17
- package/out/commands/config.d.ts +1 -0
- package/out/commands/config.js +138 -0
- package/out/commands/cost.d.ts +1 -0
- package/out/commands/cost.js +167 -0
- package/out/commands/hooks.d.ts +1 -0
- package/out/commands/hooks.js +83 -0
- package/out/commands/install.d.ts +44 -1
- package/out/commands/install.js +198 -4
- package/out/commands/instructions.d.ts +1 -0
- package/out/commands/instructions.js +113 -0
- package/out/commands/integrations.d.ts +3 -0
- package/out/commands/integrations.js +215 -0
- package/out/commands/jobs.d.ts +5 -0
- package/out/commands/jobs.js +157 -0
- package/out/commands/login.js +188 -36
- package/out/commands/memory.d.ts +3 -0
- package/out/commands/memory.js +113 -0
- package/out/commands/permissions.d.ts +1 -0
- package/out/commands/permissions.js +94 -0
- package/out/commands/plugins.d.ts +4 -0
- package/out/commands/plugins.js +192 -0
- package/out/commands/privacy.d.ts +1 -0
- package/out/commands/privacy.js +57 -0
- package/out/commands/providerKey.d.ts +32 -0
- package/out/commands/providerKey.js +108 -0
- package/out/commands/repl.d.ts +8 -1
- package/out/commands/repl.js +1207 -118
- package/out/commands/report.d.ts +39 -0
- package/out/commands/report.js +115 -0
- package/out/commands/review.d.ts +5 -0
- package/out/commands/review.js +223 -0
- package/out/commands/sessions.d.ts +23 -0
- package/out/commands/sessions.js +115 -0
- package/out/commands/settings.d.ts +3 -1
- package/out/commands/settings.js +18 -16
- package/out/commands/skills.d.ts +21 -0
- package/out/commands/skills.js +207 -0
- package/out/commands/unattended.d.ts +7 -0
- package/out/commands/unattended.js +351 -0
- package/out/commands/update.d.ts +1 -0
- package/out/commands/update.js +123 -0
- package/out/commands/worktrees.d.ts +5 -0
- package/out/commands/worktrees.js +186 -0
- package/out/executor/browser.d.ts +14 -0
- package/out/executor/browser.js +270 -0
- package/out/executor/diagnostics.d.ts +2 -0
- package/out/executor/diagnostics.js +181 -0
- package/out/executor/files.js +270 -40
- package/out/executor/git.js +42 -29
- package/out/executor/hooks.d.ts +42 -58
- package/out/executor/hooks.js +89 -182
- package/out/executor/index.d.ts +21 -5
- package/out/executor/index.js +160 -14
- package/out/executor/paths.d.ts +6 -1
- package/out/executor/paths.js +34 -6
- package/out/executor/sandbox.d.ts +40 -0
- package/out/executor/sandbox.js +299 -0
- package/out/executor/shell.d.ts +49 -4
- package/out/executor/shell.js +302 -56
- package/out/executor/toolservers.d.ts +20 -0
- package/out/executor/toolservers.js +189 -0
- package/out/executor/worktree.d.ts +9 -0
- package/out/executor/worktree.js +119 -0
- package/out/graph/read-python.js +2 -1
- package/out/lib/attach.d.ts +56 -12
- package/out/lib/attach.js +230 -63
- package/out/lib/clipboard.d.ts +23 -0
- package/out/lib/clipboard.js +182 -0
- package/out/lib/commandlist.d.ts +20 -0
- package/out/lib/commandlist.js +58 -0
- package/out/lib/decision.d.ts +22 -0
- package/out/lib/decision.js +50 -0
- package/out/lib/fingerprint.d.ts +25 -0
- package/out/lib/fingerprint.js +58 -0
- package/out/lib/gateway.d.ts +186 -2
- package/out/lib/gateway.js +59 -4
- package/out/lib/history.d.ts +24 -0
- package/out/lib/history.js +137 -0
- package/out/lib/ide.d.ts +19 -0
- package/out/lib/ide.js +131 -0
- package/out/lib/keyboard.d.ts +95 -0
- package/out/lib/keyboard.js +383 -0
- package/out/lib/machine.d.ts +21 -0
- package/out/lib/machine.js +91 -0
- package/out/lib/notify.d.ts +4 -0
- package/out/lib/notify.js +52 -0
- package/out/lib/output.d.ts +48 -0
- package/out/lib/output.js +108 -0
- package/out/lib/project-ops.d.ts +19 -0
- package/out/lib/project-ops.js +146 -0
- package/out/lib/project.d.ts +28 -0
- package/out/lib/project.js +114 -0
- package/out/lib/prompt.js +15 -2
- package/out/lib/queue.d.ts +13 -0
- package/out/lib/queue.js +116 -0
- package/out/lib/readiness.d.ts +19 -0
- package/out/lib/readiness.js +170 -1
- package/out/lib/self.d.ts +23 -0
- package/out/lib/self.js +124 -0
- package/out/lib/sessions.d.ts +19 -0
- package/out/lib/sessions.js +221 -0
- package/out/lib/stdin.d.ts +32 -0
- package/out/lib/stdin.js +117 -0
- package/out/lib/store.d.ts +40 -0
- package/out/lib/store.js +138 -0
- package/out/lib/sync.d.ts +18 -0
- package/out/lib/sync.js +81 -0
- package/out/lib/ui.d.ts +2 -4
- package/out/lib/ui.js +31 -25
- package/out/lib/voice.js +24 -0
- package/out/postinstall.js +42 -14
- package/out/providers/anthropic.d.ts +22 -0
- package/out/providers/anthropic.js +80 -0
- package/out/providers/index.d.ts +8 -0
- package/out/providers/index.js +83 -0
- package/out/providers/openai.d.ts +11 -0
- package/out/providers/openai.js +57 -0
- package/out/thin.js +609 -21
- package/package.json +9 -49
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* How a one request job writes its result for a script (manual 10.2, 10.3,
|
|
4
|
+
* Appendix B).
|
|
5
|
+
*
|
|
6
|
+
* Nothing here decides how a job ended. The service sends a reason word and
|
|
7
|
+
* the table that turns reason words into exit numbers, and this looks the word
|
|
8
|
+
* up. The only numbers known here are for the endings that happen when the
|
|
9
|
+
* service never answered: a typing mistake, no sign in, no connection, Ctrl+C.
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.FORMATS = void 0;
|
|
13
|
+
exports.outputChoice = outputChoice;
|
|
14
|
+
exports.progressToErr = progressToErr;
|
|
15
|
+
exports.exitFor = exitFor;
|
|
16
|
+
exports.localResult = localResult;
|
|
17
|
+
exports.readJsonFile = readJsonFile;
|
|
18
|
+
exports.printJson = printJson;
|
|
19
|
+
exports.unknownFlag = unknownFlag;
|
|
20
|
+
/** Endings that happen before or without the service. Appendix B numbers,
|
|
21
|
+
* which never change between versions. */
|
|
22
|
+
const LOCAL = {
|
|
23
|
+
done: 0, incomplete: 1, usage: 2, signed_out: 3, unreachable: 6, unknown: 7,
|
|
24
|
+
interrupted: 130, output_closed: 141,
|
|
25
|
+
};
|
|
26
|
+
exports.FORMATS = ['text', 'json', 'stream'];
|
|
27
|
+
/** Read `--output` and `--quiet`. Null format means a value nobody accepts. */
|
|
28
|
+
function outputChoice(args) {
|
|
29
|
+
const at = args.indexOf('--output');
|
|
30
|
+
const value = at === -1 ? 'text' : String(args[at + 1] ?? '');
|
|
31
|
+
if (!exports.FORMATS.includes(value)) {
|
|
32
|
+
return { bad: `--output takes ${exports.FORMATS.join(' or ')}.` };
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
format: value,
|
|
36
|
+
quiet: args.includes('--quiet'),
|
|
37
|
+
plain: !process.stdout.isTTY,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Whether progress belongs on standard error rather than standard output. */
|
|
41
|
+
function progressToErr(o) {
|
|
42
|
+
return o.format !== 'text' || o.quiet || o.plain;
|
|
43
|
+
}
|
|
44
|
+
/** The exit number for a reason, from the service's table when it sent one. */
|
|
45
|
+
function exitFor(reason, table) {
|
|
46
|
+
const sent = table?.[reason];
|
|
47
|
+
if (typeof sent === 'number')
|
|
48
|
+
return sent;
|
|
49
|
+
return LOCAL[reason] ?? 1;
|
|
50
|
+
}
|
|
51
|
+
/** The result object for an ending the service never saw, in its published
|
|
52
|
+
* shape: every field present, nothing known left as anything but null. */
|
|
53
|
+
function localResult(reason, error) {
|
|
54
|
+
return {
|
|
55
|
+
status: reason, error, exit: exitFor(reason), session: null, job: null, result: null,
|
|
56
|
+
verified: null, changed_files: [], refused: [],
|
|
57
|
+
cost: { actual_usd: null, baseline_usd: null, baseline_model: null }, steps: null,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Read a JSON file named on the line (`--schema`, `--config`): a permitted
|
|
62
|
+
* local read. The service decides what the contents mean.
|
|
63
|
+
*/
|
|
64
|
+
function readJsonFile(path, flagName) {
|
|
65
|
+
const fs = require('fs');
|
|
66
|
+
let text;
|
|
67
|
+
try {
|
|
68
|
+
text = fs.readFileSync(path, 'utf8');
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return { bad: `${flagName} ${path}: there is no such file.` };
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
const value = JSON.parse(text);
|
|
75
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
76
|
+
return { bad: `${flagName} ${path}: the file must hold one JSON object.` };
|
|
77
|
+
}
|
|
78
|
+
return { value: value };
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
return { bad: `${flagName} ${path}: the file is not valid JSON.` };
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Print the result object as one line of JSON on standard output. */
|
|
85
|
+
function printJson(result) {
|
|
86
|
+
process.stdout.write(JSON.stringify(result) + '\n');
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Refuse a flag this command does not know (Appendix B, exit 2).
|
|
90
|
+
*
|
|
91
|
+
* `known` lists flags that take a value; `switches` lists flags that do not.
|
|
92
|
+
* A word that is a flag's value is skipped, so a request that happens to
|
|
93
|
+
* follow `--model` is never read as a flag.
|
|
94
|
+
*/
|
|
95
|
+
function unknownFlag(args, known, switches) {
|
|
96
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
97
|
+
const a = String(args[i] ?? '');
|
|
98
|
+
if (known.includes(a)) {
|
|
99
|
+
i += 1;
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
if (switches.includes(a))
|
|
103
|
+
continue;
|
|
104
|
+
if (/^--?[A-Za-z]/.test(a) && !a.includes(' '))
|
|
105
|
+
return a;
|
|
106
|
+
}
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type GatewayReply } from "./gateway";
|
|
2
|
+
import { type ProjectFacts } from "./project";
|
|
3
|
+
export type Intent = "instructions" | "config" | "memory" | "worktrees" | "hooks";
|
|
4
|
+
export declare function useSessionFolder(folder: string | null): void;
|
|
5
|
+
/** The project for this command: the session's folder, the saved default, or
|
|
6
|
+
* the folder you are in. */
|
|
7
|
+
export declare function currentProject(): Promise<ProjectFacts>;
|
|
8
|
+
/** One file inside the project, as text, or undefined. Never a secrets file,
|
|
9
|
+
* never outside the project, never anything large. */
|
|
10
|
+
export declare function readInside(root: string, rel: string): string | undefined;
|
|
11
|
+
/** The files among `names` that exist, keyed by the name asked for. */
|
|
12
|
+
export declare function readSome(root: string, names: string[]): Record<string, string>;
|
|
13
|
+
/** Ask the service, and print what it wrote. Returns null when refused or
|
|
14
|
+
* unreachable, after saying so; the caller sets the exit status. */
|
|
15
|
+
export declare function call(intent: Intent, op: string, payload: Record<string, unknown>, facts?: ProjectFacts, quiet?: boolean): Promise<GatewayReply | null>;
|
|
16
|
+
/** Write one settings key into a project settings file, keeping everything else. */
|
|
17
|
+
export declare function writeSetting(root: string, file: string, key: string, value: unknown): void;
|
|
18
|
+
/** Open files in the person's editor and wait for it to close. */
|
|
19
|
+
export declare function openEditor(files: string[]): Promise<boolean>;
|
|
@@ -0,0 +1,146 @@
|
|
|
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.useSessionFolder = useSessionFolder;
|
|
37
|
+
exports.currentProject = currentProject;
|
|
38
|
+
exports.readInside = readInside;
|
|
39
|
+
exports.readSome = readSome;
|
|
40
|
+
exports.call = call;
|
|
41
|
+
exports.writeSetting = writeSetting;
|
|
42
|
+
exports.openEditor = openEditor;
|
|
43
|
+
/**
|
|
44
|
+
* The project operations, from a terminal (manual 2.5 to 2.11).
|
|
45
|
+
*
|
|
46
|
+
* A local helper: it reads the files the service asks for, writes the files the
|
|
47
|
+
* service says to write, and prints the lines the service wrote. Which files
|
|
48
|
+
* count, what they mean and every word shown are the service's.
|
|
49
|
+
*/
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const path = __importStar(require("node:path"));
|
|
52
|
+
const gateway_1 = require("./gateway");
|
|
53
|
+
const project_1 = require("./project");
|
|
54
|
+
const paths_1 = require("../executor/paths");
|
|
55
|
+
const ui = __importStar(require("./ui"));
|
|
56
|
+
const out = (s = "") => process.stdout.write(s + "\n");
|
|
57
|
+
/** The largest file sent. An instruction or settings file is a page, not a book. */
|
|
58
|
+
const MAX_FILE_BYTES = 64 * 1024;
|
|
59
|
+
/** The folder a session moved to with `project <path>`, when it has. */
|
|
60
|
+
let sessionFolder = null;
|
|
61
|
+
function useSessionFolder(folder) { sessionFolder = folder; }
|
|
62
|
+
/** The project for this command: the session's folder, the saved default, or
|
|
63
|
+
* the folder you are in. */
|
|
64
|
+
async function currentProject() {
|
|
65
|
+
const { currentWorkspace } = await Promise.resolve().then(() => __importStar(require("../commands/settings")));
|
|
66
|
+
const found = (0, project_1.checkFolder)(sessionFolder ?? currentWorkspace().path);
|
|
67
|
+
return (0, project_1.resolveProject)(found.ok ? found.path : process.cwd());
|
|
68
|
+
}
|
|
69
|
+
/** One file inside the project, as text, or undefined. Never a secrets file,
|
|
70
|
+
* never outside the project, never anything large. */
|
|
71
|
+
function readInside(root, rel) {
|
|
72
|
+
if ((0, paths_1.isSecret)(rel))
|
|
73
|
+
return undefined;
|
|
74
|
+
const full = path.resolve(root, rel);
|
|
75
|
+
const realRoot = fs.realpathSync(root);
|
|
76
|
+
try {
|
|
77
|
+
const real = fs.realpathSync(full);
|
|
78
|
+
if (real !== realRoot && !real.startsWith(realRoot + path.sep))
|
|
79
|
+
return undefined;
|
|
80
|
+
const st = fs.statSync(real);
|
|
81
|
+
if (!st.isFile() || st.size > MAX_FILE_BYTES)
|
|
82
|
+
return undefined;
|
|
83
|
+
return fs.readFileSync(real, "utf8");
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return undefined;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/** The files among `names` that exist, keyed by the name asked for. */
|
|
90
|
+
function readSome(root, names) {
|
|
91
|
+
const files = {};
|
|
92
|
+
for (const n of names) {
|
|
93
|
+
const body = readInside(root, n);
|
|
94
|
+
if (body !== undefined)
|
|
95
|
+
files[n] = body;
|
|
96
|
+
}
|
|
97
|
+
return files;
|
|
98
|
+
}
|
|
99
|
+
/** Ask the service, and print what it wrote. Returns null when refused or
|
|
100
|
+
* unreachable, after saying so; the caller sets the exit status. */
|
|
101
|
+
async function call(intent, op, payload, facts, quiet = false) {
|
|
102
|
+
const reply = await (0, gateway_1.ask)({
|
|
103
|
+
intent, prompt: "", op, payload: { ...(facts ? (0, project_1.asWire)(facts) : {}), ...payload },
|
|
104
|
+
});
|
|
105
|
+
if (!reply.ok || reply.served === false) {
|
|
106
|
+
out(ui.fail(reply.served === false
|
|
107
|
+
? "Your workspace is running an older build that does not answer this yet."
|
|
108
|
+
: `Your workspace could not be reached: ${reply.message || "no answer"}`));
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
if (reply.granted === false) {
|
|
112
|
+
out(ui.fail(reply.message || "Refused."));
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
if (!quiet)
|
|
116
|
+
for (const line of reply.lines ?? [])
|
|
117
|
+
out(line);
|
|
118
|
+
return reply;
|
|
119
|
+
}
|
|
120
|
+
/** Write one settings key into a project settings file, keeping everything else. */
|
|
121
|
+
function writeSetting(root, file, key, value) {
|
|
122
|
+
const full = path.join(root, file);
|
|
123
|
+
let data = {};
|
|
124
|
+
try {
|
|
125
|
+
const parsed = JSON.parse(fs.readFileSync(full, "utf8"));
|
|
126
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed))
|
|
127
|
+
data = parsed;
|
|
128
|
+
}
|
|
129
|
+
catch { /* a new file, or one that did not parse: start clean */ }
|
|
130
|
+
if (value === null || value === undefined)
|
|
131
|
+
delete data[key];
|
|
132
|
+
else
|
|
133
|
+
data[key] = value;
|
|
134
|
+
fs.mkdirSync(path.dirname(full), { recursive: true });
|
|
135
|
+
fs.writeFileSync(full, JSON.stringify(data, null, 2) + "\n");
|
|
136
|
+
}
|
|
137
|
+
/** Open files in the person's editor and wait for it to close. */
|
|
138
|
+
async function openEditor(files) {
|
|
139
|
+
const { machineSettings } = await Promise.resolve().then(() => __importStar(require("./machine")));
|
|
140
|
+
const editor = process.env.VISUAL || process.env.EDITOR || machineSettings().editor
|
|
141
|
+
|| (process.platform === "win32" ? "notepad" : "vi");
|
|
142
|
+
const { spawnSync } = await Promise.resolve().then(() => __importStar(require("node:child_process")));
|
|
143
|
+
const [cmd, ...args] = editor.split(" ").filter(Boolean);
|
|
144
|
+
const r = spawnSync(cmd, [...args, ...files], { stdio: "inherit", shell: process.platform === "win32" });
|
|
145
|
+
return r.status === 0;
|
|
146
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export interface ProjectFacts {
|
|
2
|
+
/** The repository root, or the folder itself outside a repository. Real path. */
|
|
3
|
+
root: string;
|
|
4
|
+
/** Where the person was standing, relative to `root`. Empty at the root. */
|
|
5
|
+
start: string;
|
|
6
|
+
/** Null outside a repository, or when HEAD is not on a branch. */
|
|
7
|
+
branch: string | null;
|
|
8
|
+
/** Folders the person named, real paths, never the root itself. */
|
|
9
|
+
added: string[];
|
|
10
|
+
home: string;
|
|
11
|
+
/** `owner/name` of the GitHub repository `origin` points at, or null. The
|
|
12
|
+
* service needs it for pull requests, issues and CI (manual 5.18 to 5.21). */
|
|
13
|
+
repository?: string | null;
|
|
14
|
+
}
|
|
15
|
+
/** `owner/name` from a GitHub remote address, in any of its spellings. */
|
|
16
|
+
export declare function githubRepository(remote: string | null): string | null;
|
|
17
|
+
/** A folder the person named, or the reason it cannot be used. */
|
|
18
|
+
export declare function checkFolder(raw: string): {
|
|
19
|
+
ok: true;
|
|
20
|
+
path: string;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
why: string;
|
|
24
|
+
};
|
|
25
|
+
/** The project for a job started in `startDir`, with any added folders. */
|
|
26
|
+
export declare function resolveProject(startDir: string, added?: string[]): ProjectFacts;
|
|
27
|
+
/** The facts as the service reads them, on the job and on the session line. */
|
|
28
|
+
export declare function asWire(p: ProjectFacts, mode?: string): Record<string, unknown>;
|
|
@@ -0,0 +1,114 @@
|
|
|
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.githubRepository = githubRepository;
|
|
37
|
+
exports.checkFolder = checkFolder;
|
|
38
|
+
exports.resolveProject = resolveProject;
|
|
39
|
+
exports.asWire = asWire;
|
|
40
|
+
/**
|
|
41
|
+
* Where a job works, as this machine sees it (manual 2.1 to 2.3).
|
|
42
|
+
*
|
|
43
|
+
* A local helper and nothing more: it observes the folder, the Git repository
|
|
44
|
+
* around it and the branch, because only this machine can. The service decides
|
|
45
|
+
* what those facts mean, stores them on the job and writes the line a person
|
|
46
|
+
* reads. Nothing here decides whether a path may be touched; the executor's
|
|
47
|
+
* check in `executor/paths.ts` does that immediately before each action.
|
|
48
|
+
*/
|
|
49
|
+
const node_child_process_1 = require("node:child_process");
|
|
50
|
+
const fs = __importStar(require("node:fs"));
|
|
51
|
+
const os = __importStar(require("node:os"));
|
|
52
|
+
const path = __importStar(require("node:path"));
|
|
53
|
+
/** `owner/name` from a GitHub remote address, in any of its spellings. */
|
|
54
|
+
function githubRepository(remote) {
|
|
55
|
+
// `github.com`, or an SSH alias such as `github-work` set up for a second account.
|
|
56
|
+
const m = /(?:^|[@/])github[\w.-]*[:/]([\w.-]+)\/([\w.-]+?)(?:\.git)?\/?$/.exec((remote ?? "").trim());
|
|
57
|
+
return m ? `${m[1]}/${m[2]}` : null;
|
|
58
|
+
}
|
|
59
|
+
/** A folder the person named, or the reason it cannot be used. */
|
|
60
|
+
function checkFolder(raw) {
|
|
61
|
+
const expanded = raw.replace(/^~(?=$|[\\/])/, os.homedir());
|
|
62
|
+
const resolved = path.resolve(expanded);
|
|
63
|
+
if (!fs.existsSync(resolved))
|
|
64
|
+
return { ok: false, why: `There is no folder at ${resolved}.` };
|
|
65
|
+
if (!fs.statSync(resolved).isDirectory())
|
|
66
|
+
return { ok: false, why: `${resolved} is a file, not a folder.` };
|
|
67
|
+
return { ok: true, path: fs.realpathSync(resolved) };
|
|
68
|
+
}
|
|
69
|
+
/** Git's output, or null when git failed or this is not a repository. */
|
|
70
|
+
function git(args, cwd) {
|
|
71
|
+
try {
|
|
72
|
+
// An argument list, never a shell string, so it behaves the same on Windows.
|
|
73
|
+
return (0, node_child_process_1.execFileSync)("git", args, {
|
|
74
|
+
cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** The project for a job started in `startDir`, with any added folders. */
|
|
82
|
+
function resolveProject(startDir, added = []) {
|
|
83
|
+
const start = fs.realpathSync(startDir);
|
|
84
|
+
// The way up to the repository root and the way back down, relative, rather
|
|
85
|
+
// than the root's absolute name. Git spells an absolute path its own way (on
|
|
86
|
+
// Windows, long names where the temp folder has short ones), and comparing two
|
|
87
|
+
// spellings of one folder reads as two folders.
|
|
88
|
+
const where = git(["rev-parse", "--show-cdup", "--show-prefix"], start);
|
|
89
|
+
const [up = "", down = ""] = where === null ? [] : where.split(/\r?\n/);
|
|
90
|
+
const root = where === null ? start : fs.realpathSync(path.resolve(start, up || "."));
|
|
91
|
+
const branch = where === null
|
|
92
|
+
? null : (git(["symbolic-ref", "--short", "-q", "HEAD"], root) ?? "").trim() || null;
|
|
93
|
+
const extra = [...new Set(added.map((a) => fs.realpathSync(a)))].filter((a) => a !== root);
|
|
94
|
+
return {
|
|
95
|
+
root,
|
|
96
|
+
start: down.replace(/\/+$/, ""),
|
|
97
|
+
branch,
|
|
98
|
+
added: extra,
|
|
99
|
+
home: os.homedir(),
|
|
100
|
+
repository: where === null ? null : githubRepository(git(["remote", "get-url", "origin"], root)),
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/** The facts as the service reads them, on the job and on the session line. */
|
|
104
|
+
function asWire(p, mode) {
|
|
105
|
+
return {
|
|
106
|
+
project_root: p.root,
|
|
107
|
+
start_folder: p.start,
|
|
108
|
+
branch: p.branch,
|
|
109
|
+
added_folders: p.added,
|
|
110
|
+
home: p.home,
|
|
111
|
+
...(p.repository ? { repository: p.repository } : {}),
|
|
112
|
+
...(mode ? { permission_mode: mode } : {}),
|
|
113
|
+
};
|
|
114
|
+
}
|
package/out/lib/prompt.js
CHANGED
|
@@ -35,7 +35,20 @@ function closePrompt() {
|
|
|
35
35
|
/** Ask a question and wait for the answer. */
|
|
36
36
|
function ask(prompt) {
|
|
37
37
|
return new Promise((resolve) => {
|
|
38
|
-
|
|
38
|
+
const r = open();
|
|
39
|
+
// Standard input closed before a line arrived: nothing was said. Without
|
|
40
|
+
// this the process simply ran out of work and exited 0, so a script that
|
|
41
|
+
// piped in nothing was told it had succeeded (manual Appendix B).
|
|
42
|
+
// Input that had already ended before this reader opened never says so
|
|
43
|
+
// again, so the last chance is the moment nothing else is left to wait on.
|
|
44
|
+
const closed = () => resolve('');
|
|
45
|
+
r.once('close', closed);
|
|
46
|
+
process.once('beforeExit', closed);
|
|
47
|
+
r.question(prompt, (answer) => {
|
|
48
|
+
r.off('close', closed);
|
|
49
|
+
process.off('beforeExit', closed);
|
|
50
|
+
resolve(answer.trim());
|
|
51
|
+
});
|
|
39
52
|
});
|
|
40
53
|
}
|
|
41
54
|
/**
|
|
@@ -78,7 +91,7 @@ function askHidden(prompt) {
|
|
|
78
91
|
// question on this reader would come out as dots.
|
|
79
92
|
inner._writeToOutput = asItWas;
|
|
80
93
|
process.stdout.write("\n");
|
|
81
|
-
resolve(answer
|
|
94
|
+
resolve(answer);
|
|
82
95
|
});
|
|
83
96
|
});
|
|
84
97
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type GatewayReply } from "./gateway";
|
|
2
|
+
export declare function queue(op: string, payload?: Record<string, unknown>): Promise<GatewayReply>;
|
|
3
|
+
/** The limits and mode typed on the line, for the service to hold (10.17). */
|
|
4
|
+
export declare function limitsFrom(args: string[]): Record<string, unknown>;
|
|
5
|
+
export declare const workerLog: () => string;
|
|
6
|
+
/** Whether this machine's runner is going, and its process number. */
|
|
7
|
+
export declare function workerRunning(): number | null;
|
|
8
|
+
export declare function notePid(): void;
|
|
9
|
+
/** Start the runner in the background when it is not running. */
|
|
10
|
+
export declare function ensureWorker(): {
|
|
11
|
+
pid: number;
|
|
12
|
+
started: boolean;
|
|
13
|
+
};
|
package/out/lib/queue.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
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.workerLog = void 0;
|
|
37
|
+
exports.queue = queue;
|
|
38
|
+
exports.limitsFrom = limitsFrom;
|
|
39
|
+
exports.workerRunning = workerRunning;
|
|
40
|
+
exports.notePid = notePid;
|
|
41
|
+
exports.ensureWorker = ensureWorker;
|
|
42
|
+
/**
|
|
43
|
+
* Jobs nobody is sitting at, from this side (manual 10.17 to 10.19).
|
|
44
|
+
*
|
|
45
|
+
* The service holds the schedules, the triggers and the queue, and decides what
|
|
46
|
+
* is due and what may run where. This file asks it, and keeps one runner going
|
|
47
|
+
* on this machine to take what the service offers.
|
|
48
|
+
*/
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
const gateway_1 = require("./gateway");
|
|
53
|
+
const config_1 = require("./config");
|
|
54
|
+
const self_1 = require("./self");
|
|
55
|
+
async function queue(op, payload = {}) {
|
|
56
|
+
return (0, gateway_1.ask)({
|
|
57
|
+
intent: "queue", prompt: "", op,
|
|
58
|
+
payload: { tz_minutes: -new Date().getTimezoneOffset(), ...payload },
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/** The limits and mode typed on the line, for the service to hold (10.17). */
|
|
62
|
+
function limitsFrom(args) {
|
|
63
|
+
const value = (name) => {
|
|
64
|
+
const at = args.indexOf(name);
|
|
65
|
+
return at === -1 ? undefined : args[at + 1];
|
|
66
|
+
};
|
|
67
|
+
const out = {};
|
|
68
|
+
if (value("--mode"))
|
|
69
|
+
out.mode = value("--mode");
|
|
70
|
+
if (value("--max-spend"))
|
|
71
|
+
out.max_spend = Number(value("--max-spend"));
|
|
72
|
+
if (value("--max-steps"))
|
|
73
|
+
out.max_steps = Number(value("--max-steps"));
|
|
74
|
+
if (value("--max-minutes"))
|
|
75
|
+
out.max_minutes = Number(value("--max-minutes"));
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
const pidFile = () => path.join((0, config_1.homeDir)(), "worker.pid");
|
|
79
|
+
const workerLog = () => path.join((0, config_1.homeDir)(), "worker.log");
|
|
80
|
+
exports.workerLog = workerLog;
|
|
81
|
+
function alive(pid) {
|
|
82
|
+
try {
|
|
83
|
+
process.kill(pid, 0);
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Whether this machine's runner is going, and its process number. */
|
|
91
|
+
function workerRunning() {
|
|
92
|
+
try {
|
|
93
|
+
const pid = Number(fs.readFileSync(pidFile(), "utf8").trim());
|
|
94
|
+
return pid && alive(pid) ? pid : null;
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function notePid() {
|
|
101
|
+
fs.mkdirSync((0, config_1.homeDir)(), { recursive: true });
|
|
102
|
+
fs.writeFileSync(pidFile(), String(process.pid));
|
|
103
|
+
}
|
|
104
|
+
/** Start the runner in the background when it is not running. */
|
|
105
|
+
function ensureWorker() {
|
|
106
|
+
const running = workerRunning();
|
|
107
|
+
if (running)
|
|
108
|
+
return { pid: running, started: false };
|
|
109
|
+
fs.mkdirSync((0, config_1.homeDir)(), { recursive: true });
|
|
110
|
+
const log = fs.openSync((0, exports.workerLog)(), "a");
|
|
111
|
+
const child = (0, child_process_1.spawn)(...(0, self_1.selfLaunch)(["worker"]), {
|
|
112
|
+
detached: true, stdio: ["ignore", log, log], env: { ...process.env, NO_COLOR: "1" },
|
|
113
|
+
});
|
|
114
|
+
child.unref();
|
|
115
|
+
return { pid: child.pid ?? 0, started: true };
|
|
116
|
+
}
|
package/out/lib/readiness.d.ts
CHANGED
|
@@ -28,3 +28,22 @@ export interface Readiness {
|
|
|
28
28
|
}
|
|
29
29
|
/** What this machine can and cannot do, and how to close the gap. */
|
|
30
30
|
export declare function check(): Readiness;
|
|
31
|
+
/** One checked item: its line, whether it is required, and the fix. */
|
|
32
|
+
export interface Finding {
|
|
33
|
+
ok: boolean;
|
|
34
|
+
required: boolean;
|
|
35
|
+
line: string;
|
|
36
|
+
fix?: string;
|
|
37
|
+
}
|
|
38
|
+
/** The system this is, in the words its owner would use. */
|
|
39
|
+
export declare function systemName(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Everything `comprism check` reports (manual 1.6).
|
|
42
|
+
*
|
|
43
|
+
* Required: Node 22 or newer (built in for the install script and Python
|
|
44
|
+
* installs), a sign in, and a service that answers. Anything
|
|
45
|
+
* else is optional and never makes the check fail. Whether the service still
|
|
46
|
+
* accepts this machine is asked of the service itself, on the same session
|
|
47
|
+
* operation `status` uses, so the two can never disagree.
|
|
48
|
+
*/
|
|
49
|
+
export declare function diagnose(): Promise<Finding[]>;
|