@jigging/jig 0.1.0-alpha.1 → 0.1.0-alpha.11

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.
@@ -0,0 +1,197 @@
1
+ #!/jig-runtime/bun
2
+ // @bun
3
+
4
+ // src/internal/pi-agent-launcher.ts
5
+ import {
6
+ closeSync,
7
+ mkdirSync,
8
+ openSync,
9
+ readSync,
10
+ writeFileSync,
11
+ writeSync
12
+ } from "fs";
13
+ import { spawn } from "child_process";
14
+ var STARTUP_INPUT_BYTES = 64 * 1024;
15
+ var PI_HOME = "/tmp/pi-home";
16
+ var PI_AGENT_DIR = "/tmp/pi-agent";
17
+ var CREDENTIAL_PATH = `${PI_AGENT_DIR}/auth.json`;
18
+ var SETTINGS_PATH = `${PI_AGENT_DIR}/settings.json`;
19
+ var MODELS_PATH = `${PI_AGENT_DIR}/models.json`;
20
+ var NATIVE_PI_PATH = "/agent/pi";
21
+ var ADAPTER_SPECIFIER = "./pi-acp.js";
22
+ var MAX_OUTPUT_TOKENS = 4096;
23
+ var PROVIDER = /^[a-z0-9][a-z0-9._-]{0,127}$/;
24
+ var SELECTION = /^[A-Za-z0-9][A-Za-z0-9._:/+-]{0,255}$/;
25
+ var decoder = new TextDecoder("utf-8", { fatal: true });
26
+ var PRIVATE_PI_NATIVE_ARGUMENTS = Object.freeze([
27
+ "--mode",
28
+ "rpc",
29
+ "--no-session",
30
+ "--no-tools",
31
+ "--no-extensions",
32
+ "--no-skills",
33
+ "--no-prompt-templates",
34
+ "--no-themes",
35
+ "--no-context-files",
36
+ "--no-approve",
37
+ "--offline"
38
+ ]);
39
+ var PRIVATE_PI_SETTINGS = Object.freeze({
40
+ defaultProjectTrust: "never",
41
+ enableAnalytics: false,
42
+ enableInstallTelemetry: false,
43
+ enableSkillCommands: false,
44
+ extensions: Object.freeze([]),
45
+ packages: Object.freeze([]),
46
+ prompts: Object.freeze([]),
47
+ quietStartup: true,
48
+ retry: Object.freeze({
49
+ enabled: false,
50
+ maxRetries: 0,
51
+ provider: Object.freeze({ maxRetries: 0 })
52
+ }),
53
+ skills: Object.freeze([]),
54
+ themes: Object.freeze([]),
55
+ compaction: Object.freeze({ enabled: false }),
56
+ images: Object.freeze({ blockImages: true })
57
+ });
58
+ if (import.meta.main)
59
+ await main();
60
+ async function main() {
61
+ const startup = process.env.JIG_PI_STARTUP_INPUT;
62
+ if (startup === undefined) {
63
+ await launchNativePi();
64
+ return;
65
+ }
66
+ if (startup !== "api-key" && startup !== "subscription") {
67
+ throw new Error("Pi startup input mode is invalid");
68
+ }
69
+ const provider = requireProvider(process.env.JIG_PI_PROVIDER);
70
+ const model = requireSelection(process.env.JIG_PI_MODEL, "Pi model");
71
+ mkdirSync(PI_HOME, { recursive: false, mode: 448 });
72
+ mkdirSync(PI_AGENT_DIR, { recursive: false, mode: 448 });
73
+ materializeCredential(provider, startup === "api-key" ? "api_key" : "oauth");
74
+ writeFileSync(SETTINGS_PATH, JSON.stringify(PRIVATE_PI_SETTINGS), {
75
+ encoding: "utf8",
76
+ flag: "wx",
77
+ mode: 384
78
+ });
79
+ writeFileSync(MODELS_PATH, JSON.stringify(privatePiModels(provider, model)), {
80
+ encoding: "utf8",
81
+ flag: "wx",
82
+ mode: 384
83
+ });
84
+ delete process.env.JIG_PI_STARTUP_INPUT;
85
+ await import(ADAPTER_SPECIFIER);
86
+ }
87
+ function privatePiModels(provider, model) {
88
+ return {
89
+ providers: {
90
+ [requireProvider(provider)]: {
91
+ modelOverrides: {
92
+ [requireSelection(model, "Pi model")]: { maxTokens: MAX_OUTPUT_TOKENS }
93
+ }
94
+ }
95
+ }
96
+ };
97
+ }
98
+ function materializeCredential(provider, type) {
99
+ const header = readExactly(4);
100
+ const size = new DataView(header.buffer, header.byteOffset, header.byteLength).getUint32(0, false);
101
+ if (size === 0 || size > STARTUP_INPUT_BYTES - 4) {
102
+ throw new Error("Pi startup input is invalid");
103
+ }
104
+ const credential = readExactly(size);
105
+ let descriptor;
106
+ try {
107
+ const parsed = JSON.parse(decoder.decode(credential));
108
+ if (!isRecord(parsed) || !exactKeys(parsed, [provider])) {
109
+ throw new Error("Pi startup input is invalid");
110
+ }
111
+ const entry = parsed[provider];
112
+ if (!isRecord(entry) || entry.type !== type) {
113
+ throw new Error("Pi startup input is invalid");
114
+ }
115
+ descriptor = openSync(CREDENTIAL_PATH, "wx", 384);
116
+ writeExactly(descriptor, credential);
117
+ } catch {
118
+ throw new Error("Pi startup input is invalid");
119
+ } finally {
120
+ credential.fill(0);
121
+ if (descriptor !== undefined)
122
+ closeSync(descriptor);
123
+ }
124
+ }
125
+ async function launchNativePi() {
126
+ if (process.argv.slice(2).join("\x00") !== ["--mode", "rpc", "--no-themes"].join("\x00")) {
127
+ throw new Error("pi-acp native invocation is invalid");
128
+ }
129
+ const provider = requireProvider(process.env.JIG_PI_PROVIDER);
130
+ const model = requireSelection(process.env.JIG_PI_MODEL, "Pi model");
131
+ const child = spawn(NATIVE_PI_PATH, [
132
+ ...PRIVATE_PI_NATIVE_ARGUMENTS,
133
+ "--provider",
134
+ provider,
135
+ "--model",
136
+ model
137
+ ], {
138
+ cwd: "/work",
139
+ env: process.env,
140
+ stdio: "inherit"
141
+ });
142
+ const forward = (signal) => {
143
+ if (!child.killed)
144
+ child.kill(signal);
145
+ };
146
+ process.once("SIGINT", forward);
147
+ process.once("SIGTERM", forward);
148
+ const exitCode = await new Promise((resolve, reject) => {
149
+ child.once("error", reject);
150
+ child.once("exit", (code, signal) => resolve(code ?? (signal === null ? 1 : 128)));
151
+ });
152
+ process.removeListener("SIGINT", forward);
153
+ process.removeListener("SIGTERM", forward);
154
+ process.exitCode = exitCode;
155
+ }
156
+ function readExactly(size) {
157
+ const result = new Uint8Array(size);
158
+ let offset = 0;
159
+ while (offset < result.byteLength) {
160
+ const count = readSync(0, result, offset, result.byteLength - offset, null);
161
+ if (count === 0)
162
+ throw new Error("Pi startup input ended early");
163
+ offset += count;
164
+ }
165
+ return result;
166
+ }
167
+ function writeExactly(descriptor, value) {
168
+ let offset = 0;
169
+ while (offset < value.byteLength) {
170
+ offset += writeSync(descriptor, value, offset, value.byteLength - offset, null);
171
+ }
172
+ }
173
+ function requireProvider(value) {
174
+ if (typeof value !== "string" || !PROVIDER.test(value)) {
175
+ throw new Error("Pi provider is invalid");
176
+ }
177
+ return value;
178
+ }
179
+ function requireSelection(value, label) {
180
+ if (typeof value !== "string" || !SELECTION.test(value)) {
181
+ throw new Error(`${label} is invalid`);
182
+ }
183
+ return value;
184
+ }
185
+ function exactKeys(value, expected) {
186
+ const keys = Object.keys(value).sort();
187
+ const sorted = [...expected].sort();
188
+ return keys.length === sorted.length && keys.every((key, index) => key === sorted[index]);
189
+ }
190
+ function isRecord(value) {
191
+ return value !== null && typeof value === "object" && !Array.isArray(value);
192
+ }
193
+ export {
194
+ privatePiModels,
195
+ PRIVATE_PI_SETTINGS,
196
+ PRIVATE_PI_NATIVE_ARGUMENTS
197
+ };
@@ -20,6 +20,12 @@
20
20
  "minLength": 1,
21
21
  "maxLength": 1024
22
22
  },
23
+ "targetSelector": {
24
+ "type": "string",
25
+ "description": "An exact flow:<project-relative-path> or binding:<LocalName> selector. Jig additionally validates the prefix and canonical target identity.",
26
+ "minLength": 6,
27
+ "maxLength": 1029
28
+ },
23
29
  "discoverSource": {
24
30
  "type": "object",
25
31
  "properties": {
@@ -95,12 +101,20 @@
95
101
  "settings": {
96
102
  "type": "object",
97
103
  "maxProperties": 65536
104
+ },
105
+ "slots": {
106
+ "type": "object",
107
+ "additionalProperties": {
108
+ "$ref": "#/$defs/targetSelector"
109
+ },
110
+ "maxProperties": 256
98
111
  }
99
112
  },
100
113
  "required": [
101
114
  "kind",
102
115
  "package",
103
- "settings"
116
+ "settings",
117
+ "slots"
104
118
  ],
105
119
  "additionalProperties": false
106
120
  },
@@ -1754,6 +1754,7 @@ function compareProjectPaths(left, right) {
1754
1754
  }
1755
1755
 
1756
1756
  // src/project/author.ts
1757
+ var LOCAL_NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1757
1758
  var GLOB_CHARACTERS = /[*?\[\]{}]/;
1758
1759
  function discover(roots) {
1759
1760
  const values = typeof roots === "string" ? [roots] : snapshotStringArray(roots, "roots");
@@ -1782,9 +1783,15 @@ function normalizeJig(input, canonical) {
1782
1783
  function defineBinding(input) {
1783
1784
  return normalizeBinding(input, false);
1784
1785
  }
1786
+ function flowRef(path) {
1787
+ return record({ kind: "flow", path: normalizeProjectPath(path, "Flow reference") });
1788
+ }
1789
+ function bindingRef(id) {
1790
+ return record({ kind: "binding", id: validateLocalName(id, "Binding reference") });
1791
+ }
1785
1792
  function normalizeBinding(input, canonical) {
1786
1793
  const captured = snapshotJsonObject(input, "Binding definition");
1787
- assertClosedObject(captured, canonical ? ["kind", "package", "settings"] : ["package", "settings"], "Binding definition");
1794
+ assertClosedObject(captured, canonical ? ["kind", "package", "settings", "slots"] : ["package", "settings", "slots"], "Binding definition");
1788
1795
  if (canonical && captured.kind !== "package") {
1789
1796
  throw new TypeError("Binding kind must be package");
1790
1797
  }
@@ -1792,7 +1799,30 @@ function normalizeBinding(input, canonical) {
1792
1799
  throw new TypeError("Binding package is required");
1793
1800
  const packagePath = normalizeProjectPath(captured.package, "package");
1794
1801
  const settings = Object.hasOwn(captured, "settings") ? expectJsonObject(captured.settings, "settings") : emptyRecord();
1795
- return record({ kind: "package", package: packagePath, settings });
1802
+ const slots = Object.hasOwn(captured, "slots") ? normalizeFlowSlots(captured.slots) : emptyRecord();
1803
+ return record({ kind: "package", package: packagePath, settings, slots });
1804
+ }
1805
+ function normalizeFlowSlots(value) {
1806
+ const input = snapshotJsonObject(value, "slots");
1807
+ if (Object.keys(input).length > 256) {
1808
+ throw new TypeError("slots exceed 256 entries");
1809
+ }
1810
+ const output = Object.create(null);
1811
+ for (const name of Object.keys(input).sort(compareUtf8)) {
1812
+ validateLocalName(name, "slot name");
1813
+ const target = parseRunTargetSelector(input[name], `slot ${name}`);
1814
+ output[name] = target.kind === "flow" ? `flow:${target.path}` : `binding:${target.id}`;
1815
+ }
1816
+ return Object.freeze(output);
1817
+ }
1818
+ function parseRunTargetSelector(value, label) {
1819
+ if (typeof value !== "string")
1820
+ throw new TypeError(`${label} must be a target selector`);
1821
+ if (value.startsWith("flow:"))
1822
+ return flowRef(normalizeProjectPath(value.slice(5), label));
1823
+ if (value.startsWith("binding:"))
1824
+ return bindingRef(validateLocalName(value.slice(8), label));
1825
+ throw new TypeError(`${label} must select flow:<path> or binding:<id>`);
1796
1826
  }
1797
1827
  function normalizeSource(value, field, canonical) {
1798
1828
  if (value === undefined)
@@ -1836,6 +1866,12 @@ function normalizeUniquePaths(values, label, rejectGlob) {
1836
1866
  assertNoProjectPathCollisions(paths, label);
1837
1867
  return Object.freeze(paths);
1838
1868
  }
1869
+ function validateLocalName(value, label) {
1870
+ if (typeof value !== "string" || value.length > 64 || !LOCAL_NAME.test(value)) {
1871
+ throw new TypeError(`${label} must be a LocalName`);
1872
+ }
1873
+ return value;
1874
+ }
1839
1875
  function snapshotJsonObject(value, label) {
1840
1876
  const snapshot = snapshotJson(value, label);
1841
1877
  validateJson1(snapshot);