@polylogicai/polycode 1.1.5 → 1.1.6
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/bin/polycode.mjs +11 -12
- package/lib/key-store.mjs +7 -16
- package/lib/slash-commands.mjs +5 -2
- package/package.json +1 -1
package/bin/polycode.mjs
CHANGED
|
@@ -26,7 +26,10 @@ import { fileURLToPath } from 'node:url';
|
|
|
26
26
|
const __filename = fileURLToPath(import.meta.url);
|
|
27
27
|
const __dirname = dirname(__filename);
|
|
28
28
|
const PACKAGE_JSON = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
|
|
29
|
-
|
|
29
|
+
// node:readline is intentionally NOT imported. The REPL owns stdin via the
|
|
30
|
+
// paste-aware reader in lib/paste-aware-prompt.mjs; slash commands that need
|
|
31
|
+
// masked input call readMaskedInput in lib/key-store.mjs. Any readline
|
|
32
|
+
// import here would reintroduce the v1.1.5 double-echo bug.
|
|
30
33
|
import { stdin, stdout, exit, argv, env, cwd as getCwd } from 'node:process';
|
|
31
34
|
import 'dotenv/config';
|
|
32
35
|
|
|
@@ -260,7 +263,12 @@ async function runOneShot(message, opts) {
|
|
|
260
263
|
}
|
|
261
264
|
|
|
262
265
|
async function runRepl(opts) {
|
|
263
|
-
|
|
266
|
+
// Single stdin owner. Do NOT create a readline interface here. The
|
|
267
|
+
// paste-aware reader (lib/paste-aware-prompt.mjs) owns stdin for the
|
|
268
|
+
// entire REPL lifetime, and slash commands that need line input fall
|
|
269
|
+
// through to the raw-mode readMaskedInput helper in lib/key-store.mjs.
|
|
270
|
+
// Running readline alongside a raw-mode consumer causes every keystroke
|
|
271
|
+
// to echo twice, which is the v1.1.5 double-echo bug this release fixes.
|
|
264
272
|
stdout.write(BANNER + '\n');
|
|
265
273
|
if (opts.hostedMode) {
|
|
266
274
|
stdout.write(`${C.dim}hosted tier: 60 turns/hour via polylogicai.com. Set GROQ_API_KEY for unlimited use.${C.reset}\n`);
|
|
@@ -273,10 +281,6 @@ async function runRepl(opts) {
|
|
|
273
281
|
}
|
|
274
282
|
stdout.write(`${C.dim}type /help for commands. ctrl+c or /exit to leave.${C.reset}\n\n`);
|
|
275
283
|
|
|
276
|
-
// Enable bracketed paste so multi-line pastes collapse to [Pasted #N] in
|
|
277
|
-
// the display. The readline instance above is used only as an input owner
|
|
278
|
-
// for slash commands via rl.question; the main prompt uses the custom
|
|
279
|
-
// paste-aware reader.
|
|
280
284
|
enableBracketedPaste();
|
|
281
285
|
|
|
282
286
|
try {
|
|
@@ -288,7 +292,6 @@ async function runRepl(opts) {
|
|
|
288
292
|
if (err && err.message === 'cancelled') break;
|
|
289
293
|
throw err;
|
|
290
294
|
}
|
|
291
|
-
const displayed = userInput.displayed;
|
|
292
295
|
const content = userInput.content;
|
|
293
296
|
if (!content.trim()) continue;
|
|
294
297
|
|
|
@@ -298,16 +301,13 @@ async function runRepl(opts) {
|
|
|
298
301
|
state: opts.state,
|
|
299
302
|
stdout,
|
|
300
303
|
loop: opts.loop,
|
|
301
|
-
rl,
|
|
304
|
+
rl: null,
|
|
302
305
|
});
|
|
303
306
|
if (result.exit) break;
|
|
304
307
|
stdout.write('\n');
|
|
305
308
|
continue;
|
|
306
309
|
}
|
|
307
310
|
|
|
308
|
-
// Telemetry-light logging of paste events so later /replay shows a
|
|
309
|
-
// human-readable trail. The full paste content still goes to the
|
|
310
|
-
// agent.
|
|
311
311
|
if (userInput.pastes && userInput.pastes.length > 0) {
|
|
312
312
|
for (const p of userInput.pastes) {
|
|
313
313
|
opts.canon.append('paste', { ordinal: p.ordinal, lines: p.lines, bytes: p.bytes });
|
|
@@ -319,7 +319,6 @@ async function runRepl(opts) {
|
|
|
319
319
|
}
|
|
320
320
|
} finally {
|
|
321
321
|
disableBracketedPaste();
|
|
322
|
-
rl.close();
|
|
323
322
|
}
|
|
324
323
|
}
|
|
325
324
|
|
package/lib/key-store.mjs
CHANGED
|
@@ -187,23 +187,14 @@ export async function readMaskedInput(prompt) {
|
|
|
187
187
|
// Returns { ok: true, provider, path } on success, { ok: false, reason } on
|
|
188
188
|
// failure. The caller renders the result to the user.
|
|
189
189
|
//
|
|
190
|
-
//
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
export async function runInteractiveKeyFlow({ providerHint, stdout
|
|
190
|
+
// readMaskedInput handles both the TTY case (raw-mode masked echo) and the
|
|
191
|
+
// non-TTY case (plain line read from piped stdin). The polycode REPL owns
|
|
192
|
+
// stdin via readPasteAwareLine in the main loop; each slash-command call
|
|
193
|
+
// acquires stdin via this helper, finishes its work, and cleans up so the
|
|
194
|
+
// main loop can re-acquire.
|
|
195
|
+
export async function runInteractiveKeyFlow({ providerHint, stdout } = {}) {
|
|
196
196
|
const out = stdout || process.stdout;
|
|
197
|
-
|
|
198
|
-
if (rl && typeof rl.question === 'function') {
|
|
199
|
-
// Inside the REPL: read through the active readline instance.
|
|
200
|
-
// Note: this path does not mask echo in the terminal. That is a
|
|
201
|
-
// deliberate trade-off for reliable input under the REPL. For a
|
|
202
|
-
// fully-masked prompt, use `polycode login` from the shell instead.
|
|
203
|
-
rawKey = (await rl.question('Paste your API key (or press Enter to cancel): ')).trim();
|
|
204
|
-
} else {
|
|
205
|
-
rawKey = (await readMaskedInput('Paste your API key (or press Enter to cancel): ')).trim();
|
|
206
|
-
}
|
|
197
|
+
const rawKey = (await readMaskedInput('Paste your API key (or press Enter to cancel): ')).trim();
|
|
207
198
|
if (!rawKey) {
|
|
208
199
|
return { ok: false, reason: 'cancelled' };
|
|
209
200
|
}
|
package/lib/slash-commands.mjs
CHANGED
|
@@ -20,7 +20,10 @@ ${C.bold}polycode commands${C.reset}
|
|
|
20
20
|
/exit, /quit Leave polycode
|
|
21
21
|
`;
|
|
22
22
|
|
|
23
|
-
export async function dispatchSlash(line, { canon, state, stdout, loop
|
|
23
|
+
export async function dispatchSlash(line, { canon, state, stdout, loop }) {
|
|
24
|
+
// Single stdin owner principle: no readline instance is passed in. Slash
|
|
25
|
+
// commands that need masked input fall through to the raw-mode helper
|
|
26
|
+
// in lib/key-store.mjs.
|
|
24
27
|
const [cmd, ...rest] = line.slice(1).split(/\s+/);
|
|
25
28
|
const args = rest.join(' ').trim();
|
|
26
29
|
|
|
@@ -37,7 +40,7 @@ export async function dispatchSlash(line, { canon, state, stdout, loop, rl }) {
|
|
|
37
40
|
return { continue: true };
|
|
38
41
|
}
|
|
39
42
|
stdout.write(`${C.dim}Your key will be saved locally to ~/.polycode/secrets.env (chmod 600) and never sent to the model.${C.reset}\n`);
|
|
40
|
-
const result = await runInteractiveKeyFlow({ providerHint, stdout
|
|
43
|
+
const result = await runInteractiveKeyFlow({ providerHint, stdout });
|
|
41
44
|
if (!result.ok) {
|
|
42
45
|
if (result.reason === 'cancelled') {
|
|
43
46
|
stdout.write(`${C.dim}cancelled${C.reset}\n`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polylogicai/polycode",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.6",
|
|
4
4
|
"description": "An agentic coding CLI. Runs on your machine with your keys. Every turn is appended to a SHA-256 chained session log, so your history is auditable, replayable, and portable.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "bin/polycode.mjs",
|