@illuminis/comprism 0.1.3 → 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 +61 -2
- 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 -17
- 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 +594 -32
- package/package.json +9 -49
|
@@ -0,0 +1,207 @@
|
|
|
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.SKILLS_HELP = void 0;
|
|
37
|
+
exports.skillRows = skillRows;
|
|
38
|
+
exports.cmdSkills = cmdSkills;
|
|
39
|
+
/**
|
|
40
|
+
* `comprism skills` (manual 9.1 to 9.4): the skills here, where each came
|
|
41
|
+
* from, and which one a closer skill hides; `skills add --personal <folder>`
|
|
42
|
+
* keeps a skill folder on your account.
|
|
43
|
+
*
|
|
44
|
+
* The service decides what counts as a skill, which one wins a name, and
|
|
45
|
+
* every word shown. This reads the files it names, inside the project and
|
|
46
|
+
* through the executor's own checks, lines up the columns, and for `add`
|
|
47
|
+
* reads the folder and sends it. Nothing in a skill folder is ever run.
|
|
48
|
+
*/
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const commandlist_1 = require("../lib/commandlist");
|
|
52
|
+
const gateway_1 = require("../lib/gateway");
|
|
53
|
+
const project_1 = require("../lib/project");
|
|
54
|
+
const ui = __importStar(require("../lib/ui"));
|
|
55
|
+
const ROUNDS = 6;
|
|
56
|
+
/** What a folder sent with `add` may hold, beside its SKILL.md. The service
|
|
57
|
+
* sets the same ceilings and says so when one is met. */
|
|
58
|
+
const MAX_FILES = 20;
|
|
59
|
+
const MAX_FILE_BYTES = 50_000;
|
|
60
|
+
async function skillRows(folder, op = "list") {
|
|
61
|
+
const root = (0, project_1.resolveProject)(folder).root;
|
|
62
|
+
const files = {};
|
|
63
|
+
const listings = {};
|
|
64
|
+
for (let round = 0; round < ROUNDS; round += 1) {
|
|
65
|
+
const reply = await (0, gateway_1.ask)({ intent: "skills", prompt: "", skillsPayload: { op, round, files, listings, project: root } });
|
|
66
|
+
const body = reply.skills;
|
|
67
|
+
if (!reply.ok || reply.served === false || !body) {
|
|
68
|
+
return { rows: [], error: reply.message || "The skill list could not be read from your workspace." };
|
|
69
|
+
}
|
|
70
|
+
if (!body.wants) {
|
|
71
|
+
return op === "check"
|
|
72
|
+
? { rows: [], checked: (body.rows ?? []) }
|
|
73
|
+
: { rows: body.rows ?? [], empty: body.empty };
|
|
74
|
+
}
|
|
75
|
+
for (const p of body.wants.read ?? [])
|
|
76
|
+
files[p] = (0, commandlist_1.readOne)(root, p);
|
|
77
|
+
for (const f of body.wants.list ?? [])
|
|
78
|
+
listings[f] = (0, commandlist_1.listOne)(root, f);
|
|
79
|
+
}
|
|
80
|
+
return { rows: [], error: "The skill list could not be completed." };
|
|
81
|
+
}
|
|
82
|
+
function draw(rows) {
|
|
83
|
+
const nameW = Math.min(24, Math.max(...rows.map((r) => r.name.length))) + 2;
|
|
84
|
+
const descW = Math.max(...rows.map((r) => r.description.length)) + 2;
|
|
85
|
+
return rows.map((r) => {
|
|
86
|
+
const line = `${r.name.padEnd(nameW)}${r.description.padEnd(descW)}${r.where}`;
|
|
87
|
+
const note = r.note ? ` ${r.note}` : "";
|
|
88
|
+
return r.active ? `${line}${ui.c.dim(note)}` : ui.c.dim(`${line}${note}`);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
/** Every text file in a skill folder, by path relative to it, one level of
|
|
92
|
+
* subfolder deep (a `references/` folder), within the ceilings. */
|
|
93
|
+
function folderFiles(dir) {
|
|
94
|
+
const found = {};
|
|
95
|
+
const walk = (at, rel, depth) => {
|
|
96
|
+
for (const entry of fs.readdirSync(at, { withFileTypes: true })) {
|
|
97
|
+
if (Object.keys(found).length >= MAX_FILES)
|
|
98
|
+
return;
|
|
99
|
+
if (entry.name.startsWith("."))
|
|
100
|
+
continue;
|
|
101
|
+
const full = path.join(at, entry.name);
|
|
102
|
+
const inside = rel ? `${rel}/${entry.name}` : entry.name;
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
if (depth < 1)
|
|
105
|
+
walk(full, inside, depth + 1);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (!entry.isFile() || inside === "SKILL.md")
|
|
109
|
+
continue;
|
|
110
|
+
const stat = fs.statSync(full);
|
|
111
|
+
if (stat.size > MAX_FILE_BYTES)
|
|
112
|
+
continue;
|
|
113
|
+
const text = fs.readFileSync(full, "utf8");
|
|
114
|
+
if (text.includes("\u0000"))
|
|
115
|
+
continue;
|
|
116
|
+
found[inside] = text;
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
walk(dir, "", 0);
|
|
120
|
+
return found;
|
|
121
|
+
}
|
|
122
|
+
async function addPersonal(folder) {
|
|
123
|
+
const dir = path.resolve(folder);
|
|
124
|
+
const md = path.join(dir, "SKILL.md");
|
|
125
|
+
if (!fs.existsSync(md)) {
|
|
126
|
+
process.stdout.write(` ${ui.fail(`${folder} has no SKILL.md directly inside it, so it is not a skill folder. Nothing was saved.`)}\n`);
|
|
127
|
+
return 2;
|
|
128
|
+
}
|
|
129
|
+
const reply = await (0, gateway_1.ask)({
|
|
130
|
+
intent: "skills", prompt: "",
|
|
131
|
+
skillsPayload: {
|
|
132
|
+
op: "add_personal", folder_name: path.basename(dir),
|
|
133
|
+
skill_md: fs.readFileSync(md, "utf8"), files: folderFiles(dir),
|
|
134
|
+
},
|
|
135
|
+
});
|
|
136
|
+
const body = reply.skills;
|
|
137
|
+
if (!reply.ok || reply.served === false || !body) {
|
|
138
|
+
process.stdout.write(` ${ui.fail(reply.message || "The skill could not be saved to your account.")}\n`);
|
|
139
|
+
return 1;
|
|
140
|
+
}
|
|
141
|
+
if (body.error) {
|
|
142
|
+
process.stdout.write(` ${ui.fail(body.error)}\n`);
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
process.stdout.write(` ${body.line ?? ""}\n`);
|
|
146
|
+
return 0;
|
|
147
|
+
}
|
|
148
|
+
async function removePersonal(name) {
|
|
149
|
+
const reply = await (0, gateway_1.ask)({ intent: "skills", prompt: "", skillsPayload: { op: "remove_personal", name } });
|
|
150
|
+
const body = reply.skills;
|
|
151
|
+
if (!reply.ok || reply.served === false || !body || body.error) {
|
|
152
|
+
process.stdout.write(` ${ui.fail(body?.error || reply.message || "The skill could not be removed.")}\n`);
|
|
153
|
+
return 1;
|
|
154
|
+
}
|
|
155
|
+
process.stdout.write(` ${body.line ?? ""}\n`);
|
|
156
|
+
return 0;
|
|
157
|
+
}
|
|
158
|
+
exports.SKILLS_HELP = ` comprism skills the skills here and where each came from
|
|
159
|
+
comprism skills add --personal <folder> keep a skill folder on your account
|
|
160
|
+
comprism skills remove --personal <name> remove one of your own skills
|
|
161
|
+
comprism skills check what will not load, and how to fix it
|
|
162
|
+
In a session, /name followed by anything runs that skill with it.`;
|
|
163
|
+
async function cmdSkills(args) {
|
|
164
|
+
const at = args.indexOf("--cwd");
|
|
165
|
+
const cwd = at >= 0 && args[at + 1] ? String(args[at + 1]) : process.cwd();
|
|
166
|
+
const [sub] = args.filter((a, i) => !a.startsWith("--") && args[i - 1] !== "--cwd" && args[i - 1] !== "--personal");
|
|
167
|
+
const personal = args.indexOf("--personal");
|
|
168
|
+
const target = personal >= 0 ? args[personal + 1] : undefined;
|
|
169
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
170
|
+
process.stdout.write(`${exports.SKILLS_HELP}\n`);
|
|
171
|
+
return 0;
|
|
172
|
+
}
|
|
173
|
+
if (sub === "add" || sub === "remove") {
|
|
174
|
+
if (!target) {
|
|
175
|
+
process.stdout.write(` ${ui.fail(`comprism skills ${sub} takes --personal ${sub === "add" ? "<folder>" : "<name>"}. Nothing was changed.`)}\n`);
|
|
176
|
+
return 2;
|
|
177
|
+
}
|
|
178
|
+
return sub === "add" ? addPersonal(target) : removePersonal(target);
|
|
179
|
+
}
|
|
180
|
+
if (sub === "check") {
|
|
181
|
+
const found = await skillRows(cwd, "check");
|
|
182
|
+
if (found.error) {
|
|
183
|
+
process.stdout.write(` ${ui.fail(found.error)}\n`);
|
|
184
|
+
return 1;
|
|
185
|
+
}
|
|
186
|
+
const tone = (level) => (level === "error" ? ui.c.red : level === "warn" ? ui.c.amber : ui.c.dim);
|
|
187
|
+
for (const r of found.checked ?? [])
|
|
188
|
+
process.stdout.write(` ${tone(r.level)(r.level.padEnd(7))}${r.line}\n`);
|
|
189
|
+
return (found.checked ?? []).some((r) => r.level === "error") ? 1 : 0;
|
|
190
|
+
}
|
|
191
|
+
if (sub) {
|
|
192
|
+
process.stdout.write(` ${ui.fail(`\`skills ${sub}\` is not a skills command.`)}\n${exports.SKILLS_HELP}\n`);
|
|
193
|
+
return 2;
|
|
194
|
+
}
|
|
195
|
+
const found = await skillRows(cwd);
|
|
196
|
+
if (found.error) {
|
|
197
|
+
process.stdout.write(` ${ui.fail(found.error)}\n`);
|
|
198
|
+
return 1;
|
|
199
|
+
}
|
|
200
|
+
if (!found.rows.length) {
|
|
201
|
+
process.stdout.write(` ${found.empty ?? ""}\n`);
|
|
202
|
+
return 0;
|
|
203
|
+
}
|
|
204
|
+
for (const line of draw(found.rows))
|
|
205
|
+
process.stdout.write(` ${line}\n`);
|
|
206
|
+
return 0;
|
|
207
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare function cmdSchedule(args: string[]): Promise<number>;
|
|
2
|
+
export declare function cmdTriggers(args: string[]): Promise<number>;
|
|
3
|
+
/** `comprism worker [--cloud] [--once]`: take and run what the service offers. */
|
|
4
|
+
export declare function cmdWorker(args: string[]): Promise<number>;
|
|
5
|
+
export declare function submitCloud(request: string, cwd: string, args: string[]): Promise<number>;
|
|
6
|
+
/** Before resuming a session: apply what its cloud job changed, once (10.18). */
|
|
7
|
+
export declare function handBack(sessionId: string, cwd: string): Promise<void>;
|
|
@@ -0,0 +1,351 @@
|
|
|
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.cmdSchedule = cmdSchedule;
|
|
37
|
+
exports.cmdTriggers = cmdTriggers;
|
|
38
|
+
exports.cmdWorker = cmdWorker;
|
|
39
|
+
exports.submitCloud = submitCloud;
|
|
40
|
+
exports.handBack = handBack;
|
|
41
|
+
/**
|
|
42
|
+
* `comprism schedule`, `comprism triggers`, `comprism worker`, `work --cloud`
|
|
43
|
+
* and bringing a cloud job back (manual 10.17 to 10.19).
|
|
44
|
+
*
|
|
45
|
+
* The service decides what runs, when, where and with which limits. This file
|
|
46
|
+
* sends what was typed, keeps a runner going, and does the physical part: a
|
|
47
|
+
* copy of the repository for a cloud job, the unattended job itself, and
|
|
48
|
+
* applying a cloud job's changes here when it is brought back.
|
|
49
|
+
*/
|
|
50
|
+
const fs = __importStar(require("fs"));
|
|
51
|
+
const os = __importStar(require("os"));
|
|
52
|
+
const path = __importStar(require("path"));
|
|
53
|
+
const child_process_1 = require("child_process");
|
|
54
|
+
const ui = __importStar(require("../lib/ui"));
|
|
55
|
+
const queue_1 = require("../lib/queue");
|
|
56
|
+
const project_1 = require("../lib/project");
|
|
57
|
+
const self_1 = require("../lib/self");
|
|
58
|
+
const out = (s = "") => process.stdout.write(`${s}\n`);
|
|
59
|
+
const err = (s = "") => process.stderr.write(`${s}\n`);
|
|
60
|
+
/** Values typed after flags, so they are never read as the request. */
|
|
61
|
+
const VALUE_FLAGS = ["--mode", "--max-spend", "--max-steps", "--max-minutes", "--name"];
|
|
62
|
+
function words(args) {
|
|
63
|
+
return args.filter((a, i) => !a.startsWith("--") && !VALUE_FLAGS.includes(String(args[i - 1])));
|
|
64
|
+
}
|
|
65
|
+
function failed(reply) {
|
|
66
|
+
if (!reply.ok)
|
|
67
|
+
return reply.message || "Your workspace could not be reached.";
|
|
68
|
+
if (reply.served === false)
|
|
69
|
+
return "Your workspace is running an older build that does not answer this yet.";
|
|
70
|
+
if (reply.granted === false)
|
|
71
|
+
return reply.message || "Refused.";
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
function printLines(lines) {
|
|
75
|
+
for (const line of lines ?? [])
|
|
76
|
+
out(` ${line}`);
|
|
77
|
+
}
|
|
78
|
+
function startRunner() {
|
|
79
|
+
const w = (0, queue_1.ensureWorker)();
|
|
80
|
+
out(ui.c.dim(w.started
|
|
81
|
+
? ` Started this machine's runner (process ${w.pid}); its log is ${(0, queue_1.workerLog)()}.`
|
|
82
|
+
: ` This machine's runner is going (process ${w.pid}).`));
|
|
83
|
+
}
|
|
84
|
+
// ── schedule (10.17) ─────────────────────────────────────────────────────────
|
|
85
|
+
async function cmdSchedule(args) {
|
|
86
|
+
const [verb, ...rest] = args;
|
|
87
|
+
if (!verb || verb === "list") {
|
|
88
|
+
const reply = await (0, queue_1.queue)("schedule_list");
|
|
89
|
+
const why = failed(reply);
|
|
90
|
+
if (why) {
|
|
91
|
+
err(ui.fail(why));
|
|
92
|
+
return 1;
|
|
93
|
+
}
|
|
94
|
+
out();
|
|
95
|
+
printLines(reply.lines);
|
|
96
|
+
const running = (0, queue_1.workerRunning)();
|
|
97
|
+
out(ui.c.dim(running ? ` This machine's runner is going (process ${running}).`
|
|
98
|
+
: " This machine's runner is not going; schedules here wait until it is: comprism worker"));
|
|
99
|
+
out();
|
|
100
|
+
return 0;
|
|
101
|
+
}
|
|
102
|
+
if (verb === "add") {
|
|
103
|
+
const [rule, request] = words(rest);
|
|
104
|
+
if (!rule || !request) {
|
|
105
|
+
err(ui.fail("Say when and what: comprism schedule add \"every weekday 08:00\" \"summarize failing tests\""));
|
|
106
|
+
return 2;
|
|
107
|
+
}
|
|
108
|
+
const reply = await (0, queue_1.queue)("schedule_add", {
|
|
109
|
+
rule, request, project_root: (0, project_1.resolveProject)(process.cwd()).root, options: (0, queue_1.limitsFrom)(rest),
|
|
110
|
+
});
|
|
111
|
+
const why = failed(reply);
|
|
112
|
+
if (why) {
|
|
113
|
+
err(ui.fail(why));
|
|
114
|
+
return reply.granted === false ? 2 : 1;
|
|
115
|
+
}
|
|
116
|
+
out();
|
|
117
|
+
printLines(reply.lines);
|
|
118
|
+
startRunner();
|
|
119
|
+
out();
|
|
120
|
+
return 0;
|
|
121
|
+
}
|
|
122
|
+
if (verb === "remove") {
|
|
123
|
+
const reply = await (0, queue_1.queue)("schedule_remove", { key: rest[0] ?? "" });
|
|
124
|
+
const why = failed(reply);
|
|
125
|
+
if (why) {
|
|
126
|
+
err(ui.fail(why));
|
|
127
|
+
return 1;
|
|
128
|
+
}
|
|
129
|
+
printLines(reply.lines);
|
|
130
|
+
return 0;
|
|
131
|
+
}
|
|
132
|
+
err(ui.fail(`\`schedule ${verb}\` is not a command. Use schedule, schedule add or schedule remove.`));
|
|
133
|
+
return 2;
|
|
134
|
+
}
|
|
135
|
+
// ── triggers (10.19) ─────────────────────────────────────────────────────────
|
|
136
|
+
async function cmdTriggers(args) {
|
|
137
|
+
const [verb, ...rest] = args;
|
|
138
|
+
if (!verb || verb === "list") {
|
|
139
|
+
const reply = await (0, queue_1.queue)("trigger_list");
|
|
140
|
+
const why = failed(reply);
|
|
141
|
+
if (why) {
|
|
142
|
+
err(ui.fail(why));
|
|
143
|
+
return 1;
|
|
144
|
+
}
|
|
145
|
+
out();
|
|
146
|
+
printLines(reply.lines);
|
|
147
|
+
out();
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
if (verb === "add") {
|
|
151
|
+
const [request] = words(rest);
|
|
152
|
+
if (!request) {
|
|
153
|
+
err(ui.fail("Say what each job should do: comprism triggers add \"investigate the failed CI run\""));
|
|
154
|
+
return 2;
|
|
155
|
+
}
|
|
156
|
+
const at = rest.indexOf("--name");
|
|
157
|
+
const reply = await (0, queue_1.queue)("trigger_add", {
|
|
158
|
+
request, target: "machine", name: at === -1 ? "" : rest[at + 1],
|
|
159
|
+
project_root: (0, project_1.resolveProject)(process.cwd()).root, options: (0, queue_1.limitsFrom)(rest),
|
|
160
|
+
});
|
|
161
|
+
const why = failed(reply);
|
|
162
|
+
if (why) {
|
|
163
|
+
err(ui.fail(why));
|
|
164
|
+
return reply.granted === false ? 2 : 1;
|
|
165
|
+
}
|
|
166
|
+
out();
|
|
167
|
+
printLines(reply.lines);
|
|
168
|
+
startRunner();
|
|
169
|
+
out();
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
if (verb === "remove") {
|
|
173
|
+
const reply = await (0, queue_1.queue)("trigger_remove", { key: rest[0] ?? "" });
|
|
174
|
+
const why = failed(reply);
|
|
175
|
+
if (why) {
|
|
176
|
+
err(ui.fail(why));
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
printLines(reply.lines);
|
|
180
|
+
return 0;
|
|
181
|
+
}
|
|
182
|
+
err(ui.fail(`\`triggers ${verb}\` is not a command. Use triggers, triggers add or triggers remove.`));
|
|
183
|
+
return 2;
|
|
184
|
+
}
|
|
185
|
+
function git(cwd, args, env) {
|
|
186
|
+
const r = (0, child_process_1.spawnSync)("git", args, { cwd, encoding: "utf8", env: { ...process.env, ...(env ?? {}) },
|
|
187
|
+
maxBuffer: 64 * 1024 * 1024 });
|
|
188
|
+
return { ok: r.status === 0, out: `${r.stdout ?? ""}${r.stderr ?? ""}` };
|
|
189
|
+
}
|
|
190
|
+
/** Run one claimed job as an unattended `comprism work`, in `cwd`. */
|
|
191
|
+
function runJob(job, cwd, extra = []) {
|
|
192
|
+
return new Promise((resolve) => {
|
|
193
|
+
// nosemgrep: javascript.lang.security.detect-child-process.detect-child-process -- starts this same program again, every value a separate argument, no shell
|
|
194
|
+
const child = (0, child_process_1.spawn)(...(0, self_1.selfLaunch)(["work", "--yes", "--queued", job.id,
|
|
195
|
+
"--cwd", cwd, ...extra, job.request.slice(0, 2000)]), {
|
|
196
|
+
stdio: ["ignore", "inherit", "inherit"], env: { ...process.env, NO_COLOR: "1" },
|
|
197
|
+
});
|
|
198
|
+
child.on("exit", (code) => resolve(code ?? 1));
|
|
199
|
+
child.on("error", () => resolve(1));
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
async function runCloud(job) {
|
|
203
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "comprism-cloud-"));
|
|
204
|
+
const bundle = path.join(dir, "repo.bundle");
|
|
205
|
+
const work = path.join(dir, "repo");
|
|
206
|
+
try {
|
|
207
|
+
fs.writeFileSync(bundle, Buffer.from(job.bundle ?? "", "base64"));
|
|
208
|
+
fs.mkdirSync(work);
|
|
209
|
+
const steps = [["init", "-q"], ["fetch", "-q", bundle, "refs/comprism/cloud:refs/heads/work"],
|
|
210
|
+
["checkout", "-q", "work"]];
|
|
211
|
+
for (const s of steps) {
|
|
212
|
+
const r = git(work, s);
|
|
213
|
+
if (!r.ok) {
|
|
214
|
+
await (0, queue_1.queue)("failed", { id: job.id, why: `the repository copy could not be opened: ${r.out.slice(0, 300)}` });
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const base = git(work, ["rev-parse", "HEAD"]).out.trim();
|
|
219
|
+
const { availability } = await Promise.resolve().then(() => __importStar(require("../executor/sandbox")));
|
|
220
|
+
const code = await runJob(job, work, availability().system ? ["--sandbox"] : []);
|
|
221
|
+
git(work, ["add", "-A"]);
|
|
222
|
+
const patch = git(work, ["diff", "--cached", "--binary", base]).out;
|
|
223
|
+
await (0, queue_1.queue)("result", { id: job.id, patch, result: { exit: code } });
|
|
224
|
+
out(` cloud job ${job.id.slice(0, 8)} finished with exit ${code}; ${patch ? "changes" : "no changes"} sent back`);
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
/** `comprism worker [--cloud] [--once]`: take and run what the service offers. */
|
|
231
|
+
async function cmdWorker(args) {
|
|
232
|
+
const cloud = args.includes("--cloud");
|
|
233
|
+
const once = args.includes("--once");
|
|
234
|
+
if (!cloud) {
|
|
235
|
+
const running = (0, queue_1.workerRunning)();
|
|
236
|
+
if (running && running !== process.pid) {
|
|
237
|
+
out(` This machine's runner is already going (process ${running}).`);
|
|
238
|
+
return 0;
|
|
239
|
+
}
|
|
240
|
+
(0, queue_1.notePid)();
|
|
241
|
+
}
|
|
242
|
+
out(` ${new Date().toISOString()} runner started${cloud ? " for cloud jobs" : " for this machine"}`);
|
|
243
|
+
let stop = false;
|
|
244
|
+
process.on("SIGTERM", () => { stop = true; });
|
|
245
|
+
process.on("SIGINT", () => { stop = true; });
|
|
246
|
+
while (!stop) {
|
|
247
|
+
const reply = await (0, queue_1.queue)("claim", { cloud });
|
|
248
|
+
const why = failed(reply);
|
|
249
|
+
const job = reply.data?.job ?? null;
|
|
250
|
+
if (why) {
|
|
251
|
+
out(` ${new Date().toISOString()} ${why}`);
|
|
252
|
+
}
|
|
253
|
+
else if (job) {
|
|
254
|
+
out(` ${new Date().toISOString()} running ${job.source} job ${job.id.slice(0, 8)}: ${(job.request.split("\n")[0] ?? "").slice(0, 80)}`);
|
|
255
|
+
if (cloud) {
|
|
256
|
+
await runCloud(job);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
const folder = job.project_root && fs.existsSync(job.project_root) ? job.project_root : null;
|
|
260
|
+
if (!folder) {
|
|
261
|
+
await (0, queue_1.queue)("failed", { id: job.id, why: `the project folder ${job.project_root} is not on this machine` });
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
const code = await runJob(job, folder);
|
|
265
|
+
out(` ${new Date().toISOString()} job ${job.id.slice(0, 8)} ended with exit ${code}`);
|
|
266
|
+
if (code === 1 || code === 6)
|
|
267
|
+
await (0, queue_1.queue)("failed", { id: job.id, why: `ended with exit ${code}` });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (once)
|
|
271
|
+
break;
|
|
272
|
+
continue;
|
|
273
|
+
}
|
|
274
|
+
if (once)
|
|
275
|
+
break;
|
|
276
|
+
await new Promise((r) => setTimeout(r, 15_000));
|
|
277
|
+
}
|
|
278
|
+
return 0;
|
|
279
|
+
}
|
|
280
|
+
// ── a cloud job (10.18) ──────────────────────────────────────────────────────
|
|
281
|
+
/** The repository as it is now, working changes and new files included. */
|
|
282
|
+
function repositoryCopy(root) {
|
|
283
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "comprism-copy-"));
|
|
284
|
+
const index = path.join(dir, "index");
|
|
285
|
+
const env = { GIT_INDEX_FILE: index };
|
|
286
|
+
const head = git(root, ["rev-parse", "HEAD"]);
|
|
287
|
+
if (!head.ok)
|
|
288
|
+
return { why: "this folder is not a Git repository with a commit, so it cannot be copied" };
|
|
289
|
+
git(root, ["read-tree", "HEAD"], env);
|
|
290
|
+
git(root, ["add", "-A"], env);
|
|
291
|
+
const tree = git(root, ["write-tree"], env);
|
|
292
|
+
const commit = git(root, ["commit-tree", tree.out.trim(), "-p", head.out.trim(), "-m", "comprism cloud copy"], { ...env, GIT_AUTHOR_NAME: "comprism", GIT_AUTHOR_EMAIL: "cloud@comprism",
|
|
293
|
+
GIT_COMMITTER_NAME: "comprism", GIT_COMMITTER_EMAIL: "cloud@comprism" });
|
|
294
|
+
if (!commit.ok)
|
|
295
|
+
return { why: `the working changes could not be copied: ${commit.out.slice(0, 200)}` };
|
|
296
|
+
const ref = "refs/comprism/cloud";
|
|
297
|
+
git(root, ["update-ref", ref, commit.out.trim()]);
|
|
298
|
+
const file = path.join(dir, "repo.bundle");
|
|
299
|
+
const made = git(root, ["bundle", "create", file, ref]);
|
|
300
|
+
git(root, ["update-ref", "-d", ref]);
|
|
301
|
+
return made.ok ? { file } : { why: `the copy could not be made: ${made.out.slice(0, 200)}` };
|
|
302
|
+
}
|
|
303
|
+
async function submitCloud(request, cwd, args) {
|
|
304
|
+
const root = (0, project_1.resolveProject)(cwd).root;
|
|
305
|
+
const copy = repositoryCopy(root);
|
|
306
|
+
if ("why" in copy) {
|
|
307
|
+
err(ui.fail(copy.why));
|
|
308
|
+
return 1;
|
|
309
|
+
}
|
|
310
|
+
const bundle = fs.readFileSync(copy.file).toString("base64");
|
|
311
|
+
fs.rmSync(path.dirname(copy.file), { recursive: true, force: true });
|
|
312
|
+
const reply = await (0, queue_1.queue)("submit_cloud", { request, bundle, options: (0, queue_1.limitsFrom)(args) });
|
|
313
|
+
const why = failed(reply);
|
|
314
|
+
if (why) {
|
|
315
|
+
err(ui.fail(why));
|
|
316
|
+
return reply.ok ? 1 : 6;
|
|
317
|
+
}
|
|
318
|
+
out();
|
|
319
|
+
printLines(reply.lines);
|
|
320
|
+
out();
|
|
321
|
+
return 0;
|
|
322
|
+
}
|
|
323
|
+
/** Before resuming a session: apply what its cloud job changed, once (10.18). */
|
|
324
|
+
async function handBack(sessionId, cwd) {
|
|
325
|
+
const reply = await (0, queue_1.queue)("handback", { session_id: sessionId });
|
|
326
|
+
const data = (reply.data ?? {});
|
|
327
|
+
if (!reply.ok || !data.cloud)
|
|
328
|
+
return;
|
|
329
|
+
if (!data.ready) {
|
|
330
|
+
printLines(reply.lines);
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
if (data.applied)
|
|
334
|
+
return;
|
|
335
|
+
const root = (0, project_1.resolveProject)(cwd).root;
|
|
336
|
+
if (!data.patch) {
|
|
337
|
+
out(ui.c.dim(" The cloud job changed no files."));
|
|
338
|
+
}
|
|
339
|
+
else {
|
|
340
|
+
const file = path.join(os.tmpdir(), `comprism-handback-${Date.now()}.patch`);
|
|
341
|
+
fs.writeFileSync(file, data.patch);
|
|
342
|
+
const applied = git(root, ["apply", "--3way", "--binary", file]);
|
|
343
|
+
fs.rmSync(file, { force: true });
|
|
344
|
+
if (!applied.ok) {
|
|
345
|
+
err(ui.fail(`The cloud job's changes could not be applied here: ${applied.out.slice(0, 300)}`));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
out(ui.ok("Applied the cloud job's changes to this folder."));
|
|
349
|
+
}
|
|
350
|
+
await (0, queue_1.queue)("handed_back", { id: data.id });
|
|
351
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function cmdUpdate(args: string[]): Promise<number>;
|