@illuminis/comprism 0.1.3 → 0.1.5
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 +221 -26
- package/out/agent/render.d.ts +56 -8
- package/out/agent/render.js +441 -39
- package/out/agent/session.d.ts +156 -2
- package/out/agent/session.js +795 -154
- 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 +88 -12
- 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 +249 -43
- 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 +12 -1
- package/out/commands/repl.js +1649 -122
- 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/welcome.d.ts +25 -1
- package/out/commands/welcome.js +71 -35
- 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 +190 -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 +98 -0
- package/out/lib/keyboard.js +390 -0
- package/out/lib/lastlogin.d.ts +14 -0
- package/out/lib/lastlogin.js +105 -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/picker.d.ts +1 -0
- package/out/lib/picker.js +158 -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.d.ts +3 -0
- package/out/lib/prompt.js +36 -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/startcache.d.ts +13 -0
- package/out/lib/startcache.js +78 -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 +10 -4
- package/out/lib/ui.js +65 -25
- package/out/lib/voice.d.ts +36 -0
- package/out/lib/voice.js +160 -1
- package/out/native/dictate +0 -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 +595 -33
- package/package.json +11 -50
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a one request job writes its result for a script (manual 10.2, 10.3,
|
|
3
|
+
* Appendix B).
|
|
4
|
+
*
|
|
5
|
+
* Nothing here decides how a job ended. The service sends a reason word and
|
|
6
|
+
* the table that turns reason words into exit numbers, and this looks the word
|
|
7
|
+
* up. The only numbers known here are for the endings that happen when the
|
|
8
|
+
* service never answered: a typing mistake, no sign in, no connection, Ctrl+C.
|
|
9
|
+
*/
|
|
10
|
+
export type Format = 'text' | 'json' | 'stream';
|
|
11
|
+
export interface OutputChoice {
|
|
12
|
+
format: Format;
|
|
13
|
+
/** Only the answer on standard output, even at a terminal (10.2). */
|
|
14
|
+
quiet: boolean;
|
|
15
|
+
/** Standard output is a file or a pipe: only the result goes there. */
|
|
16
|
+
plain: boolean;
|
|
17
|
+
}
|
|
18
|
+
export declare const FORMATS: Format[];
|
|
19
|
+
/** Read `--output` and `--quiet`. Null format means a value nobody accepts. */
|
|
20
|
+
export declare function outputChoice(args: string[]): OutputChoice | {
|
|
21
|
+
bad: string;
|
|
22
|
+
};
|
|
23
|
+
/** Whether progress belongs on standard error rather than standard output. */
|
|
24
|
+
export declare function progressToErr(o: OutputChoice): boolean;
|
|
25
|
+
/** The exit number for a reason, from the service's table when it sent one. */
|
|
26
|
+
export declare function exitFor(reason: string, table?: Record<string, number> | null): number;
|
|
27
|
+
/** The result object for an ending the service never saw, in its published
|
|
28
|
+
* shape: every field present, nothing known left as anything but null. */
|
|
29
|
+
export declare function localResult(reason: string, error: string): Record<string, unknown>;
|
|
30
|
+
/**
|
|
31
|
+
* Read a JSON file named on the line (`--schema`, `--config`): a permitted
|
|
32
|
+
* local read. The service decides what the contents mean.
|
|
33
|
+
*/
|
|
34
|
+
export declare function readJsonFile(path: string, flagName: string): {
|
|
35
|
+
value: Record<string, unknown>;
|
|
36
|
+
} | {
|
|
37
|
+
bad: string;
|
|
38
|
+
};
|
|
39
|
+
/** Print the result object as one line of JSON on standard output. */
|
|
40
|
+
export declare function printJson(result: Record<string, unknown>): void;
|
|
41
|
+
/**
|
|
42
|
+
* Refuse a flag this command does not know (Appendix B, exit 2).
|
|
43
|
+
*
|
|
44
|
+
* `known` lists flags that take a value; `switches` lists flags that do not.
|
|
45
|
+
* A word that is a flag's value is skipped, so a request that happens to
|
|
46
|
+
* follow `--model` is never read as a flag.
|
|
47
|
+
*/
|
|
48
|
+
export declare function unknownFlag(args: string[], known: string[], switches: string[]): string | null;
|
|
@@ -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 @@
|
|
|
1
|
+
export declare function pick(entries: Array<string | string[]>, hint: string, start?: number, numbered?: boolean): Promise<number | null>;
|
|
@@ -0,0 +1,158 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.pick = pick;
|
|
40
|
+
/**
|
|
41
|
+
* Choosing one thing from a list with the arrow keys.
|
|
42
|
+
*
|
|
43
|
+
* Each entry is one or more lines: the first is its name, the rest are quieter
|
|
44
|
+
* detail. Entries are separated by a blank line so each reads as its own item.
|
|
45
|
+
* Up and Down move, Enter picks, Esc (or Ctrl+C) leaves without picking.
|
|
46
|
+
*
|
|
47
|
+
* Only as many entries as fit the window are drawn, and the view follows the
|
|
48
|
+
* marker, so a long list scrolls instead of running off the screen (which would
|
|
49
|
+
* leave the redraw writing over lines it can no longer reach). Every line is
|
|
50
|
+
* cut to the window's width for the same reason: a wrapped line throws the
|
|
51
|
+
* redraw off by one. Off a terminal there is nothing to move through, so it
|
|
52
|
+
* returns null and the caller falls back to its typed form.
|
|
53
|
+
*
|
|
54
|
+
* With `numbered`, every entry carries its number, and typing a number moves
|
|
55
|
+
* the marker straight to it. A list of two hundred sessions is not something
|
|
56
|
+
* anybody should have to arrow through to reach the one they can already see.
|
|
57
|
+
*/
|
|
58
|
+
const node_readline_1 = __importDefault(require("node:readline"));
|
|
59
|
+
const ui = __importStar(require("./ui"));
|
|
60
|
+
const CSI = `${String.fromCharCode(27)}[`;
|
|
61
|
+
function pick(entries, hint, start = 0, numbered = false) {
|
|
62
|
+
if (!process.stdin.isTTY || !process.stdout.isTTY || entries.length === 0)
|
|
63
|
+
return Promise.resolve(null);
|
|
64
|
+
const items = entries.map((e) => (Array.isArray(e) ? e : [e]));
|
|
65
|
+
const width = Math.max(20, (process.stdout.columns || 100) - 6);
|
|
66
|
+
const cut = (s) => (s.length > width ? `${s.slice(0, width - 3)}...` : s);
|
|
67
|
+
const tall = Math.max(...items.map((i) => i.length)) + 1;
|
|
68
|
+
// Room for the entries, the hint and a line either side.
|
|
69
|
+
const fits = Math.max(1, Math.floor(((process.stdout.rows || 24) - 4) / tall));
|
|
70
|
+
let at = Math.min(Math.max(0, start), items.length - 1);
|
|
71
|
+
let top = 0;
|
|
72
|
+
let drawn = 0;
|
|
73
|
+
// The number typed so far, when the entries are numbered.
|
|
74
|
+
let typed = '';
|
|
75
|
+
const digits = String(items.length).length;
|
|
76
|
+
const label = (i, line) => (numbered ? `${String(i + 1).padStart(digits)}. ${line}` : line);
|
|
77
|
+
const draw = () => {
|
|
78
|
+
if (at < top)
|
|
79
|
+
top = at;
|
|
80
|
+
if (at >= top + fits)
|
|
81
|
+
top = at - fits + 1;
|
|
82
|
+
if (drawn)
|
|
83
|
+
process.stdout.write(`${CSI}${drawn}A\r`);
|
|
84
|
+
const lines = [];
|
|
85
|
+
if (top > 0)
|
|
86
|
+
lines.push(ui.c.dim(` ${top} more above`));
|
|
87
|
+
items.slice(top, top + fits).forEach((item, k) => {
|
|
88
|
+
const i = top + k;
|
|
89
|
+
const chosen = i === at;
|
|
90
|
+
item.forEach((line, n) => {
|
|
91
|
+
if (n === 0) {
|
|
92
|
+
const name = cut(label(i, line));
|
|
93
|
+
lines.push(chosen ? ` ${ui.c.bold(ui.c.blue(`▸ ${name}`))}` : ` ${ui.c.bold(name)}`);
|
|
94
|
+
}
|
|
95
|
+
else
|
|
96
|
+
lines.push(` ${ui.c.dim(cut(numbered ? `${' '.repeat(digits + 2)}${line}` : line))}`);
|
|
97
|
+
});
|
|
98
|
+
lines.push('');
|
|
99
|
+
});
|
|
100
|
+
const below = items.length - (top + fits);
|
|
101
|
+
if (below > 0)
|
|
102
|
+
lines.push(ui.c.dim(` ${below} more below`));
|
|
103
|
+
lines.push(` ${ui.c.dim(hint)}${typed ? ` ${ui.c.bold(`number ${typed}`)}` : ''}`);
|
|
104
|
+
// The same height every time, so the next redraw lands exactly on this one.
|
|
105
|
+
while (lines.length < drawn)
|
|
106
|
+
lines.push('');
|
|
107
|
+
process.stdout.write(`${lines.map((l) => `${CSI}2K${l}`).join('\n')}\n`);
|
|
108
|
+
drawn = lines.length;
|
|
109
|
+
};
|
|
110
|
+
return new Promise((resolve) => {
|
|
111
|
+
node_readline_1.default.emitKeypressEvents(process.stdin);
|
|
112
|
+
const wasRaw = process.stdin.isRaw;
|
|
113
|
+
process.stdin.setRawMode(true);
|
|
114
|
+
process.stdin.resume();
|
|
115
|
+
const done = (choice) => {
|
|
116
|
+
process.stdin.off('keypress', onKey);
|
|
117
|
+
process.stdin.setRawMode(wasRaw);
|
|
118
|
+
process.stdin.pause();
|
|
119
|
+
resolve(choice);
|
|
120
|
+
};
|
|
121
|
+
const onKey = (s, key) => {
|
|
122
|
+
if (!key && !(numbered && /^[0-9]$/.test(s ?? '')))
|
|
123
|
+
return;
|
|
124
|
+
if (numbered && /^[0-9]$/.test(s ?? '')) {
|
|
125
|
+
// A number past the end starts again from this digit, so a mistyped
|
|
126
|
+
// number is corrected by typing the right one, not by clearing first.
|
|
127
|
+
const next = Number(typed + s) <= items.length ? typed + s : s;
|
|
128
|
+
const n = Number(next);
|
|
129
|
+
typed = n >= 1 && n <= items.length ? next : '';
|
|
130
|
+
if (typed)
|
|
131
|
+
at = n - 1;
|
|
132
|
+
draw();
|
|
133
|
+
}
|
|
134
|
+
else if (numbered && key.name === 'backspace' && typed) {
|
|
135
|
+
typed = typed.slice(0, -1);
|
|
136
|
+
if (typed)
|
|
137
|
+
at = Number(typed) - 1;
|
|
138
|
+
draw();
|
|
139
|
+
}
|
|
140
|
+
else if (key.name === 'up' || key.name === 'k') {
|
|
141
|
+
typed = '';
|
|
142
|
+
at = Math.max(0, at - 1);
|
|
143
|
+
draw();
|
|
144
|
+
}
|
|
145
|
+
else if (key.name === 'down' || key.name === 'j') {
|
|
146
|
+
typed = '';
|
|
147
|
+
at = Math.min(items.length - 1, at + 1);
|
|
148
|
+
draw();
|
|
149
|
+
}
|
|
150
|
+
else if (key.name === 'return')
|
|
151
|
+
done(at);
|
|
152
|
+
else if (key.name === 'escape' || key.name === 'q' || (key.ctrl && key.name === 'c'))
|
|
153
|
+
done(null);
|
|
154
|
+
};
|
|
155
|
+
process.stdin.on('keypress', onKey);
|
|
156
|
+
draw();
|
|
157
|
+
});
|
|
158
|
+
}
|
|
@@ -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.d.ts
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
export declare function closePrompt(): void;
|
|
3
3
|
/** Ask a question and wait for the answer. */
|
|
4
4
|
export declare function ask(prompt: string): Promise<string>;
|
|
5
|
+
/** Ask, telling a cancel (Ctrl+C, input closed) apart from an empty answer:
|
|
6
|
+
* null is "never mind", "" is Enter. */
|
|
7
|
+
export declare function askOrCancel(prompt: string): Promise<string | null>;
|
|
5
8
|
/**
|
|
6
9
|
* Ask for a secret, and do not put it on the screen.
|
|
7
10
|
*
|