@alphacoder-v0/pi-loops 0.17.3
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/CHANGELOG.md +1989 -0
- package/LICENSE +21 -0
- package/README.md +370 -0
- package/README.zh-CN.md +606 -0
- package/docs/cli.md +252 -0
- package/docs/configuration.md +110 -0
- package/docs/design.md +155 -0
- package/docs/goal.md +59 -0
- package/docs/hooks.md +110 -0
- package/docs/loops.md +315 -0
- package/docs/mcp.md +90 -0
- package/docs/screenshot.png +0 -0
- package/docs/session-archive.md +64 -0
- package/docs/triggers.md +140 -0
- package/docs/troubleshooting.md +111 -0
- package/docs/web-ui-parity.md +305 -0
- package/examples/README.md +18 -0
- package/examples/mcp-notify-server.mjs +35 -0
- package/examples/mcp.toml +8 -0
- package/package.json +71 -0
- package/skills/pi-loops/SKILL.md +50 -0
- package/src/archive.ts +476 -0
- package/src/args.ts +217 -0
- package/src/cli-entry.mjs +23 -0
- package/src/cli.ts +610 -0
- package/src/config.ts +103 -0
- package/src/danger.ts +128 -0
- package/src/goal.ts +178 -0
- package/src/hooks.ts +436 -0
- package/src/host-control-channel.ts +243 -0
- package/src/host-control.ts +203 -0
- package/src/host-entry.mjs +14 -0
- package/src/host-runtime.ts +222 -0
- package/src/host.ts +329 -0
- package/src/inbox.ts +232 -0
- package/src/job-edit.ts +113 -0
- package/src/job-health.ts +29 -0
- package/src/lock.ts +193 -0
- package/src/log.ts +108 -0
- package/src/mcp-pool.ts +141 -0
- package/src/mcp.ts +885 -0
- package/src/paths.ts +33 -0
- package/src/pi-loops.ts +2292 -0
- package/src/pi-resolver.mjs +49 -0
- package/src/presence.ts +143 -0
- package/src/protocol.ts +198 -0
- package/src/redact.ts +61 -0
- package/src/register-pi.mjs +3 -0
- package/src/runner.ts +149 -0
- package/src/schedule.ts +431 -0
- package/src/scheduler.ts +981 -0
- package/src/sdk-runner.ts +536 -0
- package/src/share.ts +121 -0
- package/src/slots.ts +76 -0
- package/src/snapshot.ts +40 -0
- package/src/store.ts +599 -0
- package/src/subagent-guard.ts +47 -0
- package/src/thinking.ts +30 -0
- package/src/toml.ts +244 -0
- package/src/tools.ts +455 -0
- package/src/transcript.ts +84 -0
- package/src/trigger-runtime.ts +711 -0
- package/src/triggers.ts +558 -0
- package/src/trust.ts +47 -0
- package/src/ts-entry.mjs +67 -0
- package/src/ui-prefs.ts +84 -0
- package/src/version.ts +14 -0
- package/src/web.mjs +4484 -0
package/src/toml.ts
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A small TOML subset parser — enough for `mcp.toml`, `hooks.toml`
|
|
3
|
+
* and `config.toml`: comments, `[table]`, `[[array.of.tables]]`, dotted table
|
|
4
|
+
* headers, `key = value` with basic/literal strings, integers, floats, booleans,
|
|
5
|
+
* single-line arrays of scalars and one level of inline tables. Anything fancier
|
|
6
|
+
* (multi-line strings, dates, nested inline tables) throws with a line number.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type TomlValue = string | number | boolean | TomlValue[] | TomlTable;
|
|
10
|
+
export interface TomlTable {
|
|
11
|
+
[key: string]: TomlValue;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class Parser {
|
|
15
|
+
private pos = 0;
|
|
16
|
+
private readonly text: string;
|
|
17
|
+
private readonly line: number;
|
|
18
|
+
constructor(text: string, line: number) {
|
|
19
|
+
this.text = text;
|
|
20
|
+
this.line = line;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
fail(msg: string): never {
|
|
24
|
+
throw new Error(`TOML line ${this.line}: ${msg}`);
|
|
25
|
+
}
|
|
26
|
+
peek(): string {
|
|
27
|
+
return this.text[this.pos] ?? "";
|
|
28
|
+
}
|
|
29
|
+
/** Consume `ch` or fail saying where it was expected: the one way a missing `=` is reported. */
|
|
30
|
+
expect(ch: string, what: string): void {
|
|
31
|
+
if (this.peek() !== ch) this.fail(`expected ${ch} ${what}`);
|
|
32
|
+
this.pos++;
|
|
33
|
+
}
|
|
34
|
+
eof(): boolean {
|
|
35
|
+
return this.pos >= this.text.length;
|
|
36
|
+
}
|
|
37
|
+
skipWs(): void {
|
|
38
|
+
while (!this.eof() && (this.peek() === " " || this.peek() === "\t")) this.pos++;
|
|
39
|
+
}
|
|
40
|
+
rest(): string {
|
|
41
|
+
return this.text.slice(this.pos);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
parseValue(): TomlValue {
|
|
45
|
+
this.skipWs();
|
|
46
|
+
const c = this.peek();
|
|
47
|
+
if (c === '"') return this.parseBasicString();
|
|
48
|
+
if (c === "'") return this.parseLiteralString();
|
|
49
|
+
if (c === "[") return this.parseArray();
|
|
50
|
+
if (c === "{") return this.parseInlineTable();
|
|
51
|
+
const m = /^(true|false|[+-]?(?:\d[\d_]*)(?:\.\d[\d_]*)?(?:[eE][+-]?\d+)?|[+-]?(?:inf|nan))/.exec(this.rest());
|
|
52
|
+
if (!m) this.fail(`unexpected value near "${this.rest().slice(0, 20)}"`);
|
|
53
|
+
this.pos += m[0].length;
|
|
54
|
+
if (m[0] === "true") return true;
|
|
55
|
+
if (m[0] === "false") return false;
|
|
56
|
+
const n = Number(m[0].replace(/_/g, ""));
|
|
57
|
+
if (Number.isNaN(n)) this.fail(`bad number ${m[0]}`);
|
|
58
|
+
return n;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
parseBasicString(): string {
|
|
62
|
+
this.pos++; // opening quote
|
|
63
|
+
let out = "";
|
|
64
|
+
for (;;) {
|
|
65
|
+
if (this.eof()) this.fail("unterminated string");
|
|
66
|
+
const c = this.text[this.pos++];
|
|
67
|
+
if (c === '"') return out;
|
|
68
|
+
if (c === "\\") {
|
|
69
|
+
const e = this.text[this.pos++];
|
|
70
|
+
const map: Record<string, string> = { n: "\n", t: "\t", r: "\r", '"': '"', "\\": "\\", b: "\b", f: "\f" };
|
|
71
|
+
if (e in map) out += map[e];
|
|
72
|
+
else if (e === "u" || e === "U") {
|
|
73
|
+
const len = e === "u" ? 4 : 8;
|
|
74
|
+
const hex = this.text.slice(this.pos, this.pos + len);
|
|
75
|
+
if (!/^[0-9a-fA-F]+$/.test(hex) || hex.length !== len) this.fail("bad unicode escape");
|
|
76
|
+
out += String.fromCodePoint(parseInt(hex, 16));
|
|
77
|
+
this.pos += len;
|
|
78
|
+
} else this.fail(`bad escape \\${e}`);
|
|
79
|
+
} else out += c;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
parseLiteralString(): string {
|
|
84
|
+
this.pos++;
|
|
85
|
+
const end = this.text.indexOf("'", this.pos);
|
|
86
|
+
if (end < 0) this.fail("unterminated literal string");
|
|
87
|
+
const out = this.text.slice(this.pos, end);
|
|
88
|
+
this.pos = end + 1;
|
|
89
|
+
return out;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
parseArray(): TomlValue[] {
|
|
93
|
+
this.pos++;
|
|
94
|
+
const out: TomlValue[] = [];
|
|
95
|
+
for (;;) {
|
|
96
|
+
this.skipWs();
|
|
97
|
+
if (this.peek() === "]") {
|
|
98
|
+
this.pos++;
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
out.push(this.parseValue());
|
|
102
|
+
this.skipWs();
|
|
103
|
+
if (this.peek() === ",") {
|
|
104
|
+
this.pos++;
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
if (this.peek() === "]") {
|
|
108
|
+
this.pos++;
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
this.fail("expected , or ] in array");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
parseInlineTable(): TomlTable {
|
|
116
|
+
this.pos++;
|
|
117
|
+
const out: TomlTable = {};
|
|
118
|
+
for (;;) {
|
|
119
|
+
this.skipWs();
|
|
120
|
+
if (this.peek() === "}") {
|
|
121
|
+
this.pos++;
|
|
122
|
+
return out;
|
|
123
|
+
}
|
|
124
|
+
const key = this.parseKey();
|
|
125
|
+
this.skipWs();
|
|
126
|
+
this.expect("=", "in inline table");
|
|
127
|
+
out[key] = this.parseValue();
|
|
128
|
+
this.skipWs();
|
|
129
|
+
if (this.peek() === ",") {
|
|
130
|
+
this.pos++;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
if (this.peek() === "}") {
|
|
134
|
+
this.pos++;
|
|
135
|
+
return out;
|
|
136
|
+
}
|
|
137
|
+
this.fail("expected , or } in inline table");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
parseKey(): string {
|
|
142
|
+
this.skipWs();
|
|
143
|
+
if (this.peek() === '"') return this.parseBasicString();
|
|
144
|
+
if (this.peek() === "'") return this.parseLiteralString();
|
|
145
|
+
const m = /^[A-Za-z0-9_-]+/.exec(this.rest());
|
|
146
|
+
if (!m) this.fail(`bad key near "${this.rest().slice(0, 20)}"`);
|
|
147
|
+
this.pos += m[0].length;
|
|
148
|
+
return m[0];
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/** `a.b.c` → ["a","b","c"] (quoted segments allowed). */
|
|
152
|
+
parseDottedKey(): string[] {
|
|
153
|
+
const parts = [this.parseKey()];
|
|
154
|
+
this.skipWs();
|
|
155
|
+
while (this.peek() === ".") {
|
|
156
|
+
this.pos++;
|
|
157
|
+
parts.push(this.parseKey());
|
|
158
|
+
this.skipWs();
|
|
159
|
+
}
|
|
160
|
+
return parts;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
expectEnd(): void {
|
|
164
|
+
this.skipWs();
|
|
165
|
+
if (!this.eof() && this.peek() !== "#") this.fail(`unexpected trailing "${this.rest().slice(0, 20)}"`);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function stripComment(line: string): string {
|
|
170
|
+
// Remove a trailing comment that is not inside a string.
|
|
171
|
+
let inBasic = false;
|
|
172
|
+
let inLiteral = false;
|
|
173
|
+
for (let i = 0; i < line.length; i++) {
|
|
174
|
+
const c = line[i];
|
|
175
|
+
if (inBasic) {
|
|
176
|
+
if (c === "\\") i++;
|
|
177
|
+
else if (c === '"') inBasic = false;
|
|
178
|
+
} else if (inLiteral) {
|
|
179
|
+
if (c === "'") inLiteral = false;
|
|
180
|
+
} else if (c === '"') inBasic = true;
|
|
181
|
+
else if (c === "'") inLiteral = true;
|
|
182
|
+
else if (c === "#") return line.slice(0, i);
|
|
183
|
+
}
|
|
184
|
+
return line;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function descend(root: TomlTable, path: string[], lineNo: number): TomlTable {
|
|
188
|
+
let cur: TomlTable = root;
|
|
189
|
+
for (const seg of path) {
|
|
190
|
+
let next = cur[seg];
|
|
191
|
+
if (next === undefined) {
|
|
192
|
+
next = {};
|
|
193
|
+
cur[seg] = next;
|
|
194
|
+
} else if (Array.isArray(next)) {
|
|
195
|
+
const last = next[next.length - 1];
|
|
196
|
+
if (!last || typeof last !== "object" || Array.isArray(last)) throw new Error(`TOML line ${lineNo}: ${seg} is not a table`);
|
|
197
|
+
next = last;
|
|
198
|
+
} else if (typeof next !== "object") throw new Error(`TOML line ${lineNo}: ${seg} is not a table`);
|
|
199
|
+
cur = next as TomlTable;
|
|
200
|
+
}
|
|
201
|
+
return cur;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function parseToml(text: string): TomlTable {
|
|
205
|
+
const root: TomlTable = {};
|
|
206
|
+
let current: TomlTable = root;
|
|
207
|
+
const lines = text.split(/\r?\n/);
|
|
208
|
+
for (let i = 0; i < lines.length; i++) {
|
|
209
|
+
const lineNo = i + 1;
|
|
210
|
+
const raw = stripComment(lines[i]).trim();
|
|
211
|
+
if (!raw) continue;
|
|
212
|
+
if (raw.startsWith("[[")) {
|
|
213
|
+
if (!raw.endsWith("]]")) throw new Error(`TOML line ${lineNo}: bad array table header`);
|
|
214
|
+
const path = new Parser(raw.slice(2, -2), lineNo).parseDottedKey();
|
|
215
|
+
const parent = descend(root, path.slice(0, -1), lineNo);
|
|
216
|
+
const name = path[path.length - 1];
|
|
217
|
+
let arr = parent[name];
|
|
218
|
+
if (arr === undefined) {
|
|
219
|
+
arr = [];
|
|
220
|
+
parent[name] = arr;
|
|
221
|
+
}
|
|
222
|
+
if (!Array.isArray(arr)) throw new Error(`TOML line ${lineNo}: ${name} is not an array of tables`);
|
|
223
|
+
const table: TomlTable = {};
|
|
224
|
+
arr.push(table);
|
|
225
|
+
current = table;
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
if (raw.startsWith("[")) {
|
|
229
|
+
if (!raw.endsWith("]")) throw new Error(`TOML line ${lineNo}: bad table header`);
|
|
230
|
+
const path = new Parser(raw.slice(1, -1), lineNo).parseDottedKey();
|
|
231
|
+
current = descend(root, path, lineNo);
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const p = new Parser(raw, lineNo);
|
|
235
|
+
const keyPath = p.parseDottedKey();
|
|
236
|
+
p.skipWs();
|
|
237
|
+
p.expect("=", "after key");
|
|
238
|
+
const value = p.parseValue();
|
|
239
|
+
p.expectEnd();
|
|
240
|
+
const target = descend(current, keyPath.slice(0, -1), lineNo);
|
|
241
|
+
target[keyPath[keyPath.length - 1]] = value;
|
|
242
|
+
}
|
|
243
|
+
return root;
|
|
244
|
+
}
|