@polylogicai/polycode 1.1.2 → 1.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 +30 -12
- package/bin/polycode.mjs +13 -9
- package/lib/agentic.mjs +103 -14
- package/lib/hooks.mjs +7 -1
- package/lib/polycode-hosted-client.mjs +100 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,27 +1,45 @@
|
|
|
1
1
|
# polycode
|
|
2
2
|
|
|
3
|
-
An agentic coding CLI. Runs on your machine
|
|
3
|
+
An agentic coding CLI. Runs on your machine, writes a SHA-256 chained session log to disk, and ships with a free hosted tier so you can start in one command. Your history is auditable, replayable, and portable across machines.
|
|
4
4
|
|
|
5
|
-
## Install
|
|
5
|
+
## Install and run
|
|
6
|
+
|
|
7
|
+
One command, no setup, any platform:
|
|
6
8
|
|
|
7
9
|
```bash
|
|
8
|
-
|
|
10
|
+
npx @polylogicai/polycode@latest
|
|
9
11
|
```
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
That is the entire install. The first run provisions a per-install ID under `~/.polycode/` and opens an interactive session. On the free hosted tier you get 60 turns per hour for free, routed through `polylogicai.com/api/polycode/inference`. For unlimited use, set `GROQ_API_KEY` (see below) and polycode will talk to Groq directly and skip the proxy.
|
|
14
|
+
|
|
15
|
+
For repeated use, install globally:
|
|
12
16
|
|
|
13
17
|
```bash
|
|
14
|
-
|
|
18
|
+
npm install -g @polylogicai/polycode
|
|
15
19
|
polycode
|
|
16
20
|
```
|
|
17
21
|
|
|
18
|
-
|
|
22
|
+
## One-shot mode
|
|
23
|
+
|
|
24
|
+
Pass your prompt as an argument:
|
|
19
25
|
|
|
20
26
|
```bash
|
|
21
|
-
polycode "read README.md and summarize it in one sentence"
|
|
27
|
+
npx @polylogicai/polycode@latest "read README.md and summarize it in one sentence"
|
|
22
28
|
```
|
|
23
29
|
|
|
24
|
-
|
|
30
|
+
## Unlimited use with your own key
|
|
31
|
+
|
|
32
|
+
Grab a free key from `console.groq.com`, then:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# macOS and Linux
|
|
36
|
+
export GROQ_API_KEY=gsk_...
|
|
37
|
+
npx @polylogicai/polycode@latest
|
|
38
|
+
|
|
39
|
+
# Windows PowerShell
|
|
40
|
+
$env:GROQ_API_KEY = "gsk_..."
|
|
41
|
+
npx @polylogicai/polycode@latest
|
|
42
|
+
```
|
|
25
43
|
|
|
26
44
|
## Configuration
|
|
27
45
|
|
|
@@ -29,8 +47,8 @@ polycode reads configuration from environment variables and optionally from a `~
|
|
|
29
47
|
|
|
30
48
|
| Variable | Purpose | Required |
|
|
31
49
|
|---|---|---|
|
|
32
|
-
| `GROQ_API_KEY` |
|
|
33
|
-
| `ANTHROPIC_API_KEY` | Optional
|
|
50
|
+
| `GROQ_API_KEY` | Direct Groq access, unlimited. If unset, polycode uses the hosted tier. | no |
|
|
51
|
+
| `ANTHROPIC_API_KEY` | Optional higher-quality compile tier for long sessions | no |
|
|
34
52
|
| `POLYCODE_MODEL` | Override the default model | no |
|
|
35
53
|
| `POLYCODE_CWD` | Override the working directory | no |
|
|
36
54
|
|
|
@@ -92,8 +110,8 @@ Rules live in `~/.polycode/rules.yaml`. You can add your own.
|
|
|
92
110
|
## Requirements
|
|
93
111
|
|
|
94
112
|
- Node.js 20 or newer
|
|
95
|
-
- macOS or
|
|
96
|
-
-
|
|
113
|
+
- macOS, Linux, or Windows
|
|
114
|
+
- No API key required for the free hosted tier. Set `GROQ_API_KEY` for unlimited use.
|
|
97
115
|
|
|
98
116
|
## Documentation and support
|
|
99
117
|
|
package/bin/polycode.mjs
CHANGED
|
@@ -161,8 +161,8 @@ ${C.bold}Usage${C.reset}
|
|
|
161
161
|
polycode --verify Verify session history integrity
|
|
162
162
|
|
|
163
163
|
${C.bold}Environment${C.reset}
|
|
164
|
-
GROQ_API_KEY
|
|
165
|
-
ANTHROPIC_API_KEY Optional
|
|
164
|
+
GROQ_API_KEY Optional. If unset, polycode uses the free hosted tier (60 turns/hour) via polylogicai.com. Set a free key from console.groq.com for unlimited use.
|
|
165
|
+
ANTHROPIC_API_KEY Optional higher-quality compile tier for long sessions
|
|
166
166
|
POLYCODE_MODEL Override the default model
|
|
167
167
|
POLYCODE_CWD Override the working directory
|
|
168
168
|
|
|
@@ -259,6 +259,9 @@ async function runOneShot(message, opts) {
|
|
|
259
259
|
async function runRepl(opts) {
|
|
260
260
|
const rl = readline.createInterface({ input: stdin, output: stdout });
|
|
261
261
|
stdout.write(BANNER + '\n');
|
|
262
|
+
if (opts.hostedMode) {
|
|
263
|
+
stdout.write(`${C.dim}hosted tier: 60 turns/hour via polylogicai.com. Set GROQ_API_KEY for unlimited use.${C.reset}\n`);
|
|
264
|
+
}
|
|
262
265
|
if (opts.projectContextPath) {
|
|
263
266
|
stdout.write(`${C.dim}project context: ${opts.projectContextPath}${C.reset}\n`);
|
|
264
267
|
}
|
|
@@ -298,11 +301,12 @@ async function main() {
|
|
|
298
301
|
|
|
299
302
|
const configDir = resolveConfigDir();
|
|
300
303
|
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
304
|
+
// Zero-config mode: if no GROQ_API_KEY is present, run through the Polylogic
|
|
305
|
+
// hosted inference proxy at polylogicai.com/api/polycode/inference. Users
|
|
306
|
+
// who want unlimited access or their own billing can set GROQ_API_KEY to a
|
|
307
|
+
// free key from console.groq.com and the CLI will talk to Groq directly.
|
|
308
|
+
const apiKey = env.GROQ_API_KEY || '';
|
|
309
|
+
const hostedMode = !apiKey;
|
|
306
310
|
|
|
307
311
|
const rules = loadRules();
|
|
308
312
|
const model = env.POLYCODE_MODEL || 'moonshotai/kimi-k2-instruct';
|
|
@@ -356,10 +360,10 @@ async function main() {
|
|
|
356
360
|
canon_path: canonFile,
|
|
357
361
|
}, hookDir);
|
|
358
362
|
|
|
359
|
-
const loop = new AgenticLoop({ apiKey, model, rules, projectContext });
|
|
363
|
+
const loop = new AgenticLoop({ apiKey, model, rules, projectContext, hostedMode, version: VERSION });
|
|
360
364
|
const renderer = createRenderer(stdout, { verbose });
|
|
361
365
|
const state = { lastTurnTokens: 0, lastCompiler: null };
|
|
362
|
-
const opts = { loop, canon, cwd, renderer, state, sessionId, hookDir, verbose, projectContextPath };
|
|
366
|
+
const opts = { loop, canon, cwd, renderer, state, sessionId, hookDir, verbose, projectContextPath, hostedMode };
|
|
363
367
|
|
|
364
368
|
const positional = args.filter((a) => !a.startsWith('--') && args[args.indexOf(a) - 1] !== '--packet');
|
|
365
369
|
if (positional.length > 0) {
|
package/lib/agentic.mjs
CHANGED
|
@@ -13,11 +13,62 @@ import Groq from 'groq-sdk';
|
|
|
13
13
|
import { promises as fs } from 'node:fs';
|
|
14
14
|
import { exec } from 'node:child_process';
|
|
15
15
|
import { promisify } from 'node:util';
|
|
16
|
-
import { resolve, relative, sep, dirname } from 'node:path';
|
|
16
|
+
import { resolve, relative, sep, dirname, join, basename } from 'node:path';
|
|
17
17
|
import { compilePacket } from './compiler.mjs';
|
|
18
18
|
import { mintCommitment } from './commitment.mjs';
|
|
19
19
|
import { ensureActiveIntent } from './intent.mjs';
|
|
20
20
|
import { scrubSecrets } from './witness/secret-scrubber.mjs';
|
|
21
|
+
import { createHostedClient } from './polycode-hosted-client.mjs';
|
|
22
|
+
|
|
23
|
+
// Cross-platform directory walker. Pure Node.js, no shell-out. Skips common
|
|
24
|
+
// noise directories (node_modules, .git, etc.) by default. Yields absolute
|
|
25
|
+
// file paths as an async iterator so callers can bail out early.
|
|
26
|
+
export const DEFAULT_SKIP_DIRS = new Set(['node_modules', '.git', '.svn', '.hg', 'dist', 'build', '.next', '.nuxt']);
|
|
27
|
+
|
|
28
|
+
export async function* walkFiles(root, skipDirs = DEFAULT_SKIP_DIRS) {
|
|
29
|
+
let entries;
|
|
30
|
+
try {
|
|
31
|
+
entries = await fs.readdir(root, { withFileTypes: true });
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
for (const entry of entries) {
|
|
36
|
+
if (skipDirs.has(entry.name) || entry.name.startsWith('.DS_Store')) continue;
|
|
37
|
+
const fullPath = join(root, entry.name);
|
|
38
|
+
if (entry.isDirectory()) {
|
|
39
|
+
yield* walkFiles(fullPath, skipDirs);
|
|
40
|
+
} else if (entry.isFile()) {
|
|
41
|
+
yield fullPath;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Minimal glob-to-regex converter. Supports ** (any depth), * (any chars
|
|
47
|
+
// except /), ? (one char). Anchors the pattern with ^ and $ so callers get
|
|
48
|
+
// full-string matching.
|
|
49
|
+
export function globToRegex(pattern) {
|
|
50
|
+
const escaped = pattern
|
|
51
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
52
|
+
.replace(/\*\*\//g, '\u0001')
|
|
53
|
+
.replace(/\*\*/g, '\u0001')
|
|
54
|
+
.replace(/\*/g, '[^/]*')
|
|
55
|
+
.replace(/\?/g, '[^/]')
|
|
56
|
+
.replace(/\u0001/g, '(?:.*/)?');
|
|
57
|
+
return new RegExp('^' + escaped + '$');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Normalize a filesystem path to forward slashes so glob matching and output
|
|
61
|
+
// formatting behave the same on every platform.
|
|
62
|
+
export function toPosix(p) {
|
|
63
|
+
return sep === '/' ? p : p.split(sep).join('/');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Detect whether a file buffer is likely binary by looking for a NUL byte in
|
|
67
|
+
// the first kilobyte. Used by the grep tool to skip binary files.
|
|
68
|
+
export function looksBinary(content) {
|
|
69
|
+
const head = content.slice(0, 1024);
|
|
70
|
+
return head.indexOf('\u0000') !== -1;
|
|
71
|
+
}
|
|
21
72
|
|
|
22
73
|
const execAsync = promisify(exec);
|
|
23
74
|
|
|
@@ -95,7 +146,7 @@ const TOOL_SCHEMAS = [
|
|
|
95
146
|
type: 'function',
|
|
96
147
|
function: {
|
|
97
148
|
name: 'bash',
|
|
98
|
-
description: 'Run a shell command in the working directory. 30 second timeout. Output truncated at 3200 bytes.',
|
|
149
|
+
description: 'Run a shell command in the working directory. Uses the system default shell, so assume POSIX tools on macOS and Linux and cmd.exe on Windows. Prefer read_file, write_file, edit_file, glob, and grep for file operations; reserve this tool for real commands like running tests or git. 30 second timeout. Output truncated at 3200 bytes.',
|
|
99
150
|
parameters: {
|
|
100
151
|
type: 'object',
|
|
101
152
|
properties: { command: { type: 'string' } },
|
|
@@ -239,23 +290,57 @@ async function runTool(name, args, cwd) {
|
|
|
239
290
|
}
|
|
240
291
|
}
|
|
241
292
|
case 'glob': {
|
|
242
|
-
const pattern = String(args.pattern ?? '').
|
|
293
|
+
const pattern = String(args.pattern ?? '').trim();
|
|
294
|
+
if (!pattern) return '(no pattern)';
|
|
243
295
|
try {
|
|
244
|
-
const
|
|
245
|
-
|
|
296
|
+
const regex = globToRegex(pattern);
|
|
297
|
+
const matches = [];
|
|
298
|
+
for await (const fullPath of walkFiles(cwd)) {
|
|
299
|
+
const rel = toPosix(relative(cwd, fullPath));
|
|
300
|
+
if (regex.test(rel) || regex.test(basename(fullPath))) {
|
|
301
|
+
matches.push('./' + rel);
|
|
302
|
+
if (matches.length >= 200) break;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (matches.length === 0) return '(no matches)';
|
|
306
|
+
return truncateStr(matches.join('\n'));
|
|
246
307
|
} catch (err) {
|
|
247
308
|
return `error: ${err.message}`;
|
|
248
309
|
}
|
|
249
310
|
}
|
|
250
311
|
case 'grep': {
|
|
251
|
-
const pattern = String(args.pattern ?? '').
|
|
252
|
-
|
|
312
|
+
const pattern = String(args.pattern ?? '').trim();
|
|
313
|
+
if (!pattern) return '(no pattern)';
|
|
314
|
+
let regex;
|
|
253
315
|
try {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
316
|
+
regex = new RegExp(pattern);
|
|
317
|
+
} catch (err) {
|
|
318
|
+
return `error: invalid regex: ${err.message}`;
|
|
319
|
+
}
|
|
320
|
+
const globPattern = String(args.glob ?? '').trim();
|
|
321
|
+
const globRegex = globPattern ? globToRegex(globPattern) : null;
|
|
322
|
+
const matches = [];
|
|
323
|
+
try {
|
|
324
|
+
outer: for await (const fullPath of walkFiles(cwd)) {
|
|
325
|
+
const rel = toPosix(relative(cwd, fullPath));
|
|
326
|
+
if (globRegex && !(globRegex.test(rel) || globRegex.test(basename(fullPath)))) continue;
|
|
327
|
+
let content;
|
|
328
|
+
try {
|
|
329
|
+
content = await fs.readFile(fullPath, 'utf8');
|
|
330
|
+
} catch {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
333
|
+
if (looksBinary(content)) continue;
|
|
334
|
+
const lines = content.split('\n');
|
|
335
|
+
for (let i = 0; i < lines.length; i++) {
|
|
336
|
+
if (regex.test(lines[i])) {
|
|
337
|
+
matches.push(`./${rel}:${i + 1}:${lines[i].slice(0, 200)}`);
|
|
338
|
+
if (matches.length >= 100) break outer;
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (matches.length === 0) return '(no matches)';
|
|
343
|
+
return truncateStr(matches.join('\n'));
|
|
259
344
|
} catch (err) {
|
|
260
345
|
return `error: ${err.message}`;
|
|
261
346
|
}
|
|
@@ -279,17 +364,21 @@ function recoverInlineToolCalls(content) {
|
|
|
279
364
|
}
|
|
280
365
|
|
|
281
366
|
export class AgenticLoop {
|
|
282
|
-
constructor({ apiKey, model, logger, rules, projectContext } = {}) {
|
|
367
|
+
constructor({ apiKey, model, logger, rules, projectContext, hostedMode, version } = {}) {
|
|
283
368
|
this.apiKey = apiKey;
|
|
284
369
|
this.defaultModel = model || DEFAULT_MODEL;
|
|
285
370
|
this.logger = logger || console;
|
|
286
371
|
this.rules = rules || {};
|
|
287
372
|
this.systemPrompt = buildSystemPrompt(projectContext);
|
|
373
|
+
this.hostedMode = Boolean(hostedMode) || !(apiKey || process.env.GROQ_API_KEY);
|
|
374
|
+
this.version = version || 'unknown';
|
|
288
375
|
}
|
|
289
376
|
|
|
290
377
|
async runTurn({ canon, userMessage, cwd, onEvent, maxIterations }) {
|
|
291
378
|
const start = Date.now();
|
|
292
|
-
const groq =
|
|
379
|
+
const groq = this.hostedMode
|
|
380
|
+
? createHostedClient({ version: this.version })
|
|
381
|
+
: new Groq({ apiKey: this.apiKey || process.env.GROQ_API_KEY });
|
|
293
382
|
let model = this.defaultModel;
|
|
294
383
|
let didFallback = false;
|
|
295
384
|
const limit = maxIterations || DEFAULT_MAX_ITERATIONS;
|
package/lib/hooks.mjs
CHANGED
|
@@ -72,11 +72,17 @@ export async function fireHook(eventName, payload, hookDir) {
|
|
|
72
72
|
const isShell = hookPath.endsWith('.sh') || !hookPath.includes('.');
|
|
73
73
|
const isNode = hookPath.endsWith('.mjs') || hookPath.endsWith('.js');
|
|
74
74
|
|
|
75
|
+
// On Windows we cannot spawn sh for POSIX shell hooks. Skip with a clear
|
|
76
|
+
// reason so the caller knows the hook did not run. Node hooks are portable.
|
|
77
|
+
if (isShell && process.platform === 'win32') {
|
|
78
|
+
return { action: 'allow', reason: 'shell hooks not supported on Windows; use .mjs' };
|
|
79
|
+
}
|
|
80
|
+
|
|
75
81
|
return new Promise((resolveHook) => {
|
|
76
82
|
let child;
|
|
77
83
|
try {
|
|
78
84
|
child = isNode
|
|
79
|
-
? spawn(
|
|
85
|
+
? spawn(process.execPath, [hookPath], { stdio: ['pipe', 'pipe', 'pipe'] })
|
|
80
86
|
: spawn('sh', [hookPath], { stdio: ['pipe', 'pipe', 'pipe'] });
|
|
81
87
|
} catch (err) {
|
|
82
88
|
resolveHook({ action: 'allow', reason: `spawn failed: ${err.message}` });
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
// lib/polycode-hosted-client.mjs
|
|
2
|
+
// Hosted inference client for users who have not provided a GROQ_API_KEY.
|
|
3
|
+
// Posts chat-completions requests to the Polylogic proxy at
|
|
4
|
+
// polylogicai.com/api/polycode/inference, which forwards them to Groq using
|
|
5
|
+
// a server-side key. Rate limited to 60 turns per install per hour. Power
|
|
6
|
+
// users can skip the proxy by setting GROQ_API_KEY, in which case the agent
|
|
7
|
+
// talks to Groq directly.
|
|
8
|
+
//
|
|
9
|
+
// The returned object mimics the subset of the groq-sdk surface that the
|
|
10
|
+
// agentic loop uses (chat.completions.create), so the call sites do not need
|
|
11
|
+
// to know which backend is in play.
|
|
12
|
+
|
|
13
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
14
|
+
import { homedir } from 'node:os';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { randomUUID } from 'node:crypto';
|
|
17
|
+
|
|
18
|
+
const DEFAULT_PROXY_URL = 'https://polylogicai.com/api/polycode/inference';
|
|
19
|
+
const CONFIG_DIR = join(homedir(), '.polycode');
|
|
20
|
+
const INSTALL_ID_PATH = join(CONFIG_DIR, 'install_id');
|
|
21
|
+
|
|
22
|
+
// Produce a stable per-install identifier. Created on first run, persisted
|
|
23
|
+
// to ~/.polycode/install_id so subsequent runs reuse the same ID for rate
|
|
24
|
+
// limiting. The file contains a bare UUID with no PII.
|
|
25
|
+
export function getOrCreateInstallId() {
|
|
26
|
+
try {
|
|
27
|
+
if (existsSync(INSTALL_ID_PATH)) {
|
|
28
|
+
const existing = readFileSync(INSTALL_ID_PATH, 'utf8').trim();
|
|
29
|
+
if (/^[A-Za-z0-9_-]{8,64}$/.test(existing)) return existing;
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
// fall through and regenerate
|
|
33
|
+
}
|
|
34
|
+
if (!existsSync(CONFIG_DIR)) {
|
|
35
|
+
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const fresh = randomUUID();
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(INSTALL_ID_PATH, fresh + '\n', { mode: 0o600 });
|
|
40
|
+
} catch {
|
|
41
|
+
// non-fatal; rate limiting will fall back to per-IP
|
|
42
|
+
}
|
|
43
|
+
return fresh;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Minimal Groq-SDK-shaped client that posts every request to the Polylogic
|
|
47
|
+
// proxy. The shape matches what lib/agentic.mjs calls: an object with a
|
|
48
|
+
// `chat.completions.create(payload)` method returning an OpenAI-compatible
|
|
49
|
+
// chat completion object.
|
|
50
|
+
export function createHostedClient({ version, proxyUrl } = {}) {
|
|
51
|
+
const url = proxyUrl || process.env.POLYCODE_PROXY_URL || DEFAULT_PROXY_URL;
|
|
52
|
+
const installId = getOrCreateInstallId();
|
|
53
|
+
const headers = {
|
|
54
|
+
'Content-Type': 'application/json',
|
|
55
|
+
'X-Polycode-Install-ID': installId,
|
|
56
|
+
'X-Polycode-Version': version || 'unknown',
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
async function createCompletion(payload) {
|
|
60
|
+
let res;
|
|
61
|
+
try {
|
|
62
|
+
res = await fetch(url, {
|
|
63
|
+
method: 'POST',
|
|
64
|
+
headers,
|
|
65
|
+
body: JSON.stringify(payload),
|
|
66
|
+
});
|
|
67
|
+
} catch (err) {
|
|
68
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
69
|
+
const wrapped = new Error(
|
|
70
|
+
`polycode hosted inference unreachable at ${url}: ${msg}. Set GROQ_API_KEY to a free key from console.groq.com to bypass the proxy.`
|
|
71
|
+
);
|
|
72
|
+
wrapped.cause = err;
|
|
73
|
+
throw wrapped;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const text = await res.text();
|
|
77
|
+
if (!res.ok) {
|
|
78
|
+
let parsed;
|
|
79
|
+
try { parsed = JSON.parse(text); } catch { parsed = null; }
|
|
80
|
+
const serverMsg = parsed?.error || text || `HTTP ${res.status}`;
|
|
81
|
+
throw new Error(`polycode hosted inference error (${res.status}): ${serverMsg}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
return JSON.parse(text);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
throw new Error(`polycode hosted inference returned non-JSON body: ${text.slice(0, 200)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
chat: {
|
|
93
|
+
completions: {
|
|
94
|
+
create: createCompletion,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
_proxyUrl: url,
|
|
98
|
+
_installId: installId,
|
|
99
|
+
};
|
|
100
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@polylogicai/polycode",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
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",
|