@arnilo/prism 0.0.4 → 0.0.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/CHANGELOG.md +18 -0
- package/README.md +34 -10
- package/dist/agents.js +146 -19
- package/dist/cli-init.d.ts +41 -0
- package/dist/cli-init.js +390 -0
- package/dist/cli-runner.d.ts +7 -1
- package/dist/cli-runner.js +13 -1
- package/dist/content.d.ts +19 -0
- package/dist/content.js +197 -69
- package/dist/contracts.d.ts +94 -9
- package/dist/contracts.js +8 -0
- package/dist/feedback.d.ts +48 -0
- package/dist/feedback.js +230 -0
- package/dist/index.d.ts +6 -4
- package/dist/index.js +4 -3
- package/dist/providers/media.d.ts +3 -1
- package/dist/providers/media.js +11 -1
- package/dist/testing/feedback.d.ts +6 -0
- package/dist/testing/feedback.js +37 -0
- package/dist/testing/persistence-schema.d.ts +3 -3
- package/dist/testing/persistence-schema.js +32 -2
- package/dist/testing/run-ledger-conformance.js +7 -1
- package/docs/a2a.md +73 -0
- package/docs/agent-events.md +4 -6
- package/docs/agent-loops.md +1 -1
- package/docs/agent-session-runtime.md +14 -16
- package/docs/cli-rpc.md +35 -7
- package/docs/coding-agent-tools.md +2 -2
- package/docs/coding-security.md +7 -3
- package/docs/compaction-observational-memory.md +2 -0
- package/docs/context-and-skills.md +1 -0
- package/docs/credentials-and-redaction.md +2 -2
- package/docs/database-persistence.md +9 -6
- package/docs/evaluations.md +122 -0
- package/docs/extensions.md +2 -2
- package/docs/host-security.md +20 -3
- package/docs/index.md +29 -17
- package/docs/mcp-tools.md +49 -4
- package/docs/migration.md +33 -3
- package/docs/multimodal-content.md +14 -6
- package/docs/observability.md +14 -6
- package/docs/performance.md +209 -0
- package/docs/postgres-persistence.md +6 -4
- package/docs/provider-conformance.md +1 -0
- package/docs/provider-packages.md +2 -0
- package/docs/providers/ai-sdk.md +113 -0
- package/docs/public-contracts.md +6 -5
- package/docs/rag.md +113 -0
- package/docs/release-and-install.md +100 -77
- package/docs/review-coverage-2026-07-15.md +193 -0
- package/docs/runs-and-usage.md +41 -4
- package/docs/server.md +139 -0
- package/docs/settings-auth-trust-security.md +5 -5
- package/docs/sqlite-persistence.md +4 -3
- package/docs/supervisors.md +71 -0
- package/docs/workflow-orchestration-primitives.md +19 -3
- package/docs/workflows.md +97 -23
- package/docs/working-and-semantic-memory.md +169 -0
- package/package.json +12 -2
- package/templates/init/README.md.tmpl +28 -0
- package/templates/init/env.example.tmpl +1 -0
- package/templates/init/gitignore.tmpl +11 -0
- package/templates/init/optional/evals-example.ts.tmpl +17 -0
- package/templates/init/optional/workflows-example.ts.tmpl +27 -0
- package/templates/init/package.json.tmpl +22 -0
- package/templates/init/providers.json +76 -0
- package/templates/init/src/agent.ts.tmpl +10 -0
- package/templates/init/src/index.ts.tmpl +12 -0
- package/templates/init/src/tests/agent.test.ts.tmpl +24 -0
- package/templates/init/tsconfig.json.tmpl +15 -0
package/dist/cli-init.js
ADDED
|
@@ -0,0 +1,390 @@
|
|
|
1
|
+
import { access, mkdir, readdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { constants as fsConstants, readFileSync } from "node:fs";
|
|
3
|
+
import { dirname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
4
|
+
import { fileURLToPath } from "node:url";
|
|
5
|
+
export class InitUsageError extends Error {
|
|
6
|
+
}
|
|
7
|
+
let cachedCatalog;
|
|
8
|
+
let cachedProviderIds;
|
|
9
|
+
function providersCatalogPath(templatesRoot) {
|
|
10
|
+
return join(templatesRoot, "providers.json");
|
|
11
|
+
}
|
|
12
|
+
function loadProvidersCatalog(templatesRoot = defaultTemplatesRoot()) {
|
|
13
|
+
if (cachedCatalog && templatesRoot === defaultTemplatesRoot())
|
|
14
|
+
return cachedCatalog;
|
|
15
|
+
const raw = JSON.parse(readFileSync(providersCatalogPath(templatesRoot), "utf8"));
|
|
16
|
+
const map = new Map();
|
|
17
|
+
for (const [id, spec] of Object.entries(raw)) {
|
|
18
|
+
if (!id || typeof spec !== "object" || spec === null) {
|
|
19
|
+
throw new Error(`Invalid init provider catalog entry: ${id}`);
|
|
20
|
+
}
|
|
21
|
+
map.set(id, { ...spec, id });
|
|
22
|
+
}
|
|
23
|
+
if (map.size === 0)
|
|
24
|
+
throw new Error("Init provider catalog is empty");
|
|
25
|
+
if (templatesRoot === defaultTemplatesRoot()) {
|
|
26
|
+
cachedCatalog = map;
|
|
27
|
+
cachedProviderIds = Object.freeze([...map.keys()]);
|
|
28
|
+
}
|
|
29
|
+
return map;
|
|
30
|
+
}
|
|
31
|
+
/** Provider ids supported by `prism init --provider`. Loaded from templates data. */
|
|
32
|
+
export function listInitProviders(templatesRoot) {
|
|
33
|
+
if (!templatesRoot && cachedProviderIds)
|
|
34
|
+
return cachedProviderIds;
|
|
35
|
+
return Object.freeze([...loadProvidersCatalog(templatesRoot).keys()]);
|
|
36
|
+
}
|
|
37
|
+
/** @deprecated Prefer listInitProviders(); retained for tests that import the name. */
|
|
38
|
+
export const INIT_PROVIDERS = listInitProviders();
|
|
39
|
+
export function getInitUsage(templatesRoot) {
|
|
40
|
+
const providers = listInitProviders(templatesRoot).join("|");
|
|
41
|
+
return `Usage: prism init <dir> [options]
|
|
42
|
+
|
|
43
|
+
Create a minimal TypeScript Prism project with one explicit provider and an offline mock test.
|
|
44
|
+
|
|
45
|
+
Arguments:
|
|
46
|
+
<dir> Destination directory (created if missing)
|
|
47
|
+
|
|
48
|
+
Options:
|
|
49
|
+
--provider <name> Provider id (${providers}). Default: mock
|
|
50
|
+
--with-workflows Add optional workflows dependency and example
|
|
51
|
+
--with-evals Add optional evals dependency and example
|
|
52
|
+
--force Overwrite existing generated files
|
|
53
|
+
-h, --help Show this help
|
|
54
|
+
|
|
55
|
+
Examples:
|
|
56
|
+
prism init my-agent
|
|
57
|
+
prism init my-agent --provider openai
|
|
58
|
+
prism init my-agent --provider openai --with-workflows
|
|
59
|
+
`;
|
|
60
|
+
}
|
|
61
|
+
export const initUsage = getInitUsage();
|
|
62
|
+
export function parseInitArgs(argv, templatesRoot) {
|
|
63
|
+
let directory;
|
|
64
|
+
let provider = "mock";
|
|
65
|
+
let withWorkflows = false;
|
|
66
|
+
let withEvals = false;
|
|
67
|
+
let force = false;
|
|
68
|
+
let help = false;
|
|
69
|
+
const providers = listInitProviders(templatesRoot);
|
|
70
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
71
|
+
const arg = argv[i];
|
|
72
|
+
if (arg === "-h" || arg === "--help") {
|
|
73
|
+
help = true;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (arg === "--force") {
|
|
77
|
+
force = true;
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (arg === "--with-workflows") {
|
|
81
|
+
withWorkflows = true;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (arg === "--with-evals") {
|
|
85
|
+
withEvals = true;
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (arg === "--provider") {
|
|
89
|
+
const value = argv[i + 1];
|
|
90
|
+
if (value === undefined || value.startsWith("-")) {
|
|
91
|
+
throw new InitUsageError("Missing value for --provider");
|
|
92
|
+
}
|
|
93
|
+
i += 1;
|
|
94
|
+
if (!providers.includes(value)) {
|
|
95
|
+
throw new InitUsageError(`Unknown provider: ${value}. Expected one of ${providers.join(", ")}`);
|
|
96
|
+
}
|
|
97
|
+
provider = value;
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (arg.startsWith("-")) {
|
|
101
|
+
throw new InitUsageError(`Unknown flag: ${arg}`);
|
|
102
|
+
}
|
|
103
|
+
if (directory !== undefined) {
|
|
104
|
+
throw new InitUsageError(`Unexpected argument: ${arg}`);
|
|
105
|
+
}
|
|
106
|
+
directory = arg;
|
|
107
|
+
}
|
|
108
|
+
if (!help && directory === undefined) {
|
|
109
|
+
throw new InitUsageError("Missing destination directory");
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
directory: directory ?? ".",
|
|
113
|
+
provider,
|
|
114
|
+
withWorkflows,
|
|
115
|
+
withEvals,
|
|
116
|
+
force,
|
|
117
|
+
help,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
export async function runInitCommand(argv, runtime) {
|
|
121
|
+
let options;
|
|
122
|
+
try {
|
|
123
|
+
options = parseInitArgs(argv, runtime.templatesRoot);
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
write(runtime.stderr, `${error instanceof Error ? error.message : String(error)}\n${getInitUsage(runtime.templatesRoot)}`);
|
|
127
|
+
return 2;
|
|
128
|
+
}
|
|
129
|
+
if (options.help) {
|
|
130
|
+
write(runtime.stdout, getInitUsage(runtime.templatesRoot));
|
|
131
|
+
return 0;
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const result = await createInitProject(options, runtime);
|
|
135
|
+
write(runtime.stdout, [
|
|
136
|
+
`Created Prism project in ${result.targetDir}`,
|
|
137
|
+
` provider: ${result.provider}`,
|
|
138
|
+
` files: ${result.writtenFiles.length}`,
|
|
139
|
+
` bytes: ${result.totalBytes}`,
|
|
140
|
+
...(result.withWorkflows ? [" optional: workflows"] : []),
|
|
141
|
+
...(result.withEvals ? [" optional: evals"] : []),
|
|
142
|
+
"",
|
|
143
|
+
"Next:",
|
|
144
|
+
` cd ${shellQuote(displayPath(result.targetDir, runtime.cwd ?? process.cwd()))}`,
|
|
145
|
+
" npm install",
|
|
146
|
+
" npm test",
|
|
147
|
+
"",
|
|
148
|
+
].join("\n"));
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
153
|
+
if (error instanceof InitUsageError) {
|
|
154
|
+
write(runtime.stderr, `${message}\n${getInitUsage(runtime.templatesRoot)}`);
|
|
155
|
+
return 2;
|
|
156
|
+
}
|
|
157
|
+
write(runtime.stderr, `${message}\n`);
|
|
158
|
+
return 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
export async function createInitProject(options, runtime = {
|
|
162
|
+
stdout: process.stdout,
|
|
163
|
+
stderr: process.stderr,
|
|
164
|
+
}) {
|
|
165
|
+
const cwd = runtime.cwd ?? process.cwd();
|
|
166
|
+
const targetDir = resolveInitDirectory(options.directory, cwd);
|
|
167
|
+
const templatesRoot = runtime.templatesRoot ?? defaultTemplatesRoot();
|
|
168
|
+
const version = runtime.packageVersion ?? (await readPackageVersion());
|
|
169
|
+
const catalog = loadProvidersCatalog(templatesRoot);
|
|
170
|
+
const provider = catalog.get(options.provider);
|
|
171
|
+
if (!provider) {
|
|
172
|
+
throw new InitUsageError(`Unknown provider: ${options.provider}. Expected one of ${[...catalog.keys()].join(", ")}`);
|
|
173
|
+
}
|
|
174
|
+
const projectName = sanitizePackageName(basenameSafe(targetDir));
|
|
175
|
+
await assertDestinationWritable(targetDir, options.force);
|
|
176
|
+
const tokens = buildTokens({
|
|
177
|
+
projectName,
|
|
178
|
+
version,
|
|
179
|
+
provider,
|
|
180
|
+
withWorkflows: options.withWorkflows,
|
|
181
|
+
withEvals: options.withEvals,
|
|
182
|
+
});
|
|
183
|
+
const planned = await planFiles({
|
|
184
|
+
templatesRoot,
|
|
185
|
+
withWorkflows: options.withWorkflows,
|
|
186
|
+
withEvals: options.withEvals,
|
|
187
|
+
});
|
|
188
|
+
if (!options.force) {
|
|
189
|
+
for (const file of planned) {
|
|
190
|
+
const dest = join(targetDir, file.relativePath);
|
|
191
|
+
if (await exists(dest)) {
|
|
192
|
+
throw new InitUsageError(`Refusing to overwrite existing file: ${file.relativePath} (pass --force to overwrite)`);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
const writtenFiles = [];
|
|
197
|
+
let totalBytes = 0;
|
|
198
|
+
for (const file of planned) {
|
|
199
|
+
const dest = join(targetDir, file.relativePath);
|
|
200
|
+
assertPathInside(targetDir, dest);
|
|
201
|
+
const raw = await readFile(file.sourcePath, "utf8");
|
|
202
|
+
const content = applyTokens(raw, tokens);
|
|
203
|
+
await mkdir(dirname(dest), { recursive: true });
|
|
204
|
+
await writeFile(dest, content, "utf8");
|
|
205
|
+
writtenFiles.push(file.relativePath);
|
|
206
|
+
totalBytes += Buffer.byteLength(content, "utf8");
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
targetDir,
|
|
210
|
+
writtenFiles,
|
|
211
|
+
provider: options.provider,
|
|
212
|
+
withWorkflows: options.withWorkflows,
|
|
213
|
+
withEvals: options.withEvals,
|
|
214
|
+
totalBytes,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
export function defaultTemplatesRoot() {
|
|
218
|
+
return join(dirname(fileURLToPath(import.meta.url)), "..", "templates", "init");
|
|
219
|
+
}
|
|
220
|
+
export function isInitProvider(value, templatesRoot) {
|
|
221
|
+
return listInitProviders(templatesRoot).includes(value);
|
|
222
|
+
}
|
|
223
|
+
function resolveInitDirectory(input, cwd) {
|
|
224
|
+
const trimmed = input.trim();
|
|
225
|
+
if (!trimmed)
|
|
226
|
+
throw new InitUsageError("Missing destination directory");
|
|
227
|
+
if (trimmed.includes("\0"))
|
|
228
|
+
throw new InitUsageError("Invalid destination directory");
|
|
229
|
+
// Reject Windows drive-relative oddities and keep the path normal.
|
|
230
|
+
const normalizedInput = normalize(trimmed);
|
|
231
|
+
if (normalizedInput.split(sep).includes("..") && !isAbsolute(normalizedInput)) {
|
|
232
|
+
// Allow intentional relative parents, but still resolve and validate after join.
|
|
233
|
+
}
|
|
234
|
+
const resolved = resolve(cwd, normalizedInput);
|
|
235
|
+
if (resolved === sep || /^[A-Za-z]:[\\/]?$/.test(resolved)) {
|
|
236
|
+
throw new InitUsageError(`Refusing to scaffold into filesystem root: ${resolved}`);
|
|
237
|
+
}
|
|
238
|
+
return resolved;
|
|
239
|
+
}
|
|
240
|
+
async function assertDestinationWritable(targetDir, force) {
|
|
241
|
+
if (!(await exists(targetDir))) {
|
|
242
|
+
await mkdir(targetDir, { recursive: true });
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const entries = await readdir(targetDir);
|
|
246
|
+
if (entries.length > 0 && !force) {
|
|
247
|
+
throw new InitUsageError(`Destination is not empty: ${targetDir} (pass --force to overwrite generated files)`);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
async function planFiles(options) {
|
|
251
|
+
const root = options.templatesRoot;
|
|
252
|
+
const files = [
|
|
253
|
+
{ relativePath: "package.json", sourcePath: join(root, "package.json.tmpl") },
|
|
254
|
+
{ relativePath: "tsconfig.json", sourcePath: join(root, "tsconfig.json.tmpl") },
|
|
255
|
+
{ relativePath: ".gitignore", sourcePath: join(root, "gitignore.tmpl") },
|
|
256
|
+
{ relativePath: ".env.example", sourcePath: join(root, "env.example.tmpl") },
|
|
257
|
+
{ relativePath: "README.md", sourcePath: join(root, "README.md.tmpl") },
|
|
258
|
+
{ relativePath: "src/agent.ts", sourcePath: join(root, "src/agent.ts.tmpl") },
|
|
259
|
+
{ relativePath: "src/index.ts", sourcePath: join(root, "src/index.ts.tmpl") },
|
|
260
|
+
{ relativePath: "src/__tests__/agent.test.ts", sourcePath: join(root, "src/tests/agent.test.ts.tmpl") },
|
|
261
|
+
];
|
|
262
|
+
if (options.withWorkflows) {
|
|
263
|
+
files.push({
|
|
264
|
+
relativePath: "src/workflows-example.ts",
|
|
265
|
+
sourcePath: join(root, "optional/workflows-example.ts.tmpl"),
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
if (options.withEvals) {
|
|
269
|
+
files.push({
|
|
270
|
+
relativePath: "src/evals-example.ts",
|
|
271
|
+
sourcePath: join(root, "optional/evals-example.ts.tmpl"),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
for (const file of files) {
|
|
275
|
+
try {
|
|
276
|
+
await access(file.sourcePath, fsConstants.R_OK);
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
throw new Error(`Missing init template: ${file.sourcePath}`);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
return files;
|
|
283
|
+
}
|
|
284
|
+
function buildTokens(input) {
|
|
285
|
+
const dependencies = {
|
|
286
|
+
"@arnilo/prism": input.version,
|
|
287
|
+
};
|
|
288
|
+
if (input.provider.packageName) {
|
|
289
|
+
dependencies[input.provider.packageName] = input.version;
|
|
290
|
+
}
|
|
291
|
+
if (input.withWorkflows) {
|
|
292
|
+
dependencies["@arnilo/prism-workflows"] = input.version;
|
|
293
|
+
}
|
|
294
|
+
if (input.withEvals) {
|
|
295
|
+
dependencies["@arnilo/prism-evals"] = input.version;
|
|
296
|
+
}
|
|
297
|
+
const dependencyLines = Object.entries(dependencies)
|
|
298
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
299
|
+
.map(([name, ver]) => ` ${JSON.stringify(name)}: ${JSON.stringify(ver)}`)
|
|
300
|
+
.join(",\n");
|
|
301
|
+
const envBlock = input.provider.envKey
|
|
302
|
+
? `# Placeholder only — never commit real secrets.\n${input.provider.envKey}=${input.provider.envPlaceholder ?? ""}\n`
|
|
303
|
+
: `# No API key required for the mock provider.\n# Switch providers with: prism init <dir> --provider openai --force\n`;
|
|
304
|
+
const nextStepsLive = input.provider.envKey
|
|
305
|
+
? `2. Copy \`.env.example\` to \`.env\` and set \`${input.provider.envKey}\`.\n3. Run \`npm start\` for a live provider call.`
|
|
306
|
+
: `2. Run \`npm start\` (mock provider; no network or credentials).`;
|
|
307
|
+
const optionalDocs = [
|
|
308
|
+
...(input.withWorkflows
|
|
309
|
+
? ["- `src/workflows-example.ts` — tiny DAG example using `@arnilo/prism-workflows`."]
|
|
310
|
+
: []),
|
|
311
|
+
...(input.withEvals
|
|
312
|
+
? ["- `src/evals-example.ts` — deterministic scorer example using `@arnilo/prism-evals`."]
|
|
313
|
+
: []),
|
|
314
|
+
].join("\n");
|
|
315
|
+
return {
|
|
316
|
+
__PROJECT_NAME__: input.projectName,
|
|
317
|
+
__PRISM_VERSION__: input.version,
|
|
318
|
+
__PROVIDER_ID__: input.provider.id,
|
|
319
|
+
__PROVIDER_PACKAGE__: input.provider.packageName ?? "",
|
|
320
|
+
__DEPENDENCIES__: dependencyLines,
|
|
321
|
+
__PROVIDER_IMPORTS__: input.provider.imports,
|
|
322
|
+
__PROVIDER_EXPRESSION__: input.provider.providerExpression,
|
|
323
|
+
__MODEL_EXPRESSION__: input.provider.modelExpression,
|
|
324
|
+
__ENV_EXAMPLE__: envBlock,
|
|
325
|
+
__NEXT_STEPS_LIVE__: nextStepsLive,
|
|
326
|
+
__OPTIONAL_DOCS__: optionalDocs.length > 0 ? `${optionalDocs}\n` : "",
|
|
327
|
+
__PROVIDER_README_NOTE__: input.provider.packageName
|
|
328
|
+
? `Selected provider package: \`${input.provider.packageName}\` (model \`${input.provider.modelName}\`).`
|
|
329
|
+
: "Selected provider: built-in `mock` (offline demos and tests).",
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
function applyTokens(template, tokens) {
|
|
333
|
+
let out = template;
|
|
334
|
+
for (const [token, value] of Object.entries(tokens)) {
|
|
335
|
+
out = out.split(token).join(value);
|
|
336
|
+
}
|
|
337
|
+
if (/__[A-Z0-9_]+__/.test(out)) {
|
|
338
|
+
const leftover = out.match(/__[A-Z0-9_]+__/g) ?? [];
|
|
339
|
+
throw new Error(`Unresolved init template tokens: ${Array.from(new Set(leftover)).join(", ")}`);
|
|
340
|
+
}
|
|
341
|
+
return out;
|
|
342
|
+
}
|
|
343
|
+
async function readPackageVersion() {
|
|
344
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
345
|
+
const pkg = JSON.parse(await readFile(pkgPath, "utf8"));
|
|
346
|
+
if (!pkg.version)
|
|
347
|
+
throw new Error(`Missing version in ${pkgPath}`);
|
|
348
|
+
return pkg.version;
|
|
349
|
+
}
|
|
350
|
+
function assertPathInside(root, candidate) {
|
|
351
|
+
const rel = relative(root, candidate);
|
|
352
|
+
if (rel === "" || (!rel.startsWith("..") && !isAbsolute(rel)))
|
|
353
|
+
return;
|
|
354
|
+
throw new InitUsageError(`Refusing to write outside destination: ${candidate}`);
|
|
355
|
+
}
|
|
356
|
+
async function exists(path) {
|
|
357
|
+
try {
|
|
358
|
+
await access(path, fsConstants.F_OK);
|
|
359
|
+
return true;
|
|
360
|
+
}
|
|
361
|
+
catch {
|
|
362
|
+
return false;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
function sanitizePackageName(name) {
|
|
366
|
+
const cleaned = name
|
|
367
|
+
.toLowerCase()
|
|
368
|
+
.replace(/[^a-z0-9._-]+/g, "-")
|
|
369
|
+
.replace(/^-+|-+$/g, "");
|
|
370
|
+
return cleaned.length > 0 ? cleaned : "prism-agent";
|
|
371
|
+
}
|
|
372
|
+
function basenameSafe(path) {
|
|
373
|
+
const parts = path.split(/[\\/]/).filter(Boolean);
|
|
374
|
+
return parts[parts.length - 1] ?? "prism-agent";
|
|
375
|
+
}
|
|
376
|
+
function displayPath(targetDir, cwd) {
|
|
377
|
+
const rel = relative(cwd, targetDir);
|
|
378
|
+
if (rel && !rel.startsWith("..") && !isAbsolute(rel))
|
|
379
|
+
return rel || ".";
|
|
380
|
+
return targetDir;
|
|
381
|
+
}
|
|
382
|
+
function shellQuote(value) {
|
|
383
|
+
if (/^[A-Za-z0-9_./:@-]+$/.test(value))
|
|
384
|
+
return value;
|
|
385
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
386
|
+
}
|
|
387
|
+
function write(stream, text) {
|
|
388
|
+
stream.write(text);
|
|
389
|
+
}
|
|
390
|
+
//# sourceMappingURL=cli-init.js.map
|
package/dist/cli-runner.d.ts
CHANGED
|
@@ -60,8 +60,14 @@ export interface CliRuntime {
|
|
|
60
60
|
/** Optional global root for SYSTEM.md auto-load (host-controlled). No default;
|
|
61
61
|
* the CLI does not auto-touch the user's home directory. */
|
|
62
62
|
readonly globalRoot?: string;
|
|
63
|
+
/** Override init template root (tests). */
|
|
64
|
+
readonly initTemplatesRoot?: string;
|
|
65
|
+
/** Override version stamped by `prism init` (tests). */
|
|
66
|
+
readonly initPackageVersion?: string;
|
|
67
|
+
/** Working directory for relative `prism init` destinations (tests). */
|
|
68
|
+
readonly cwd?: string;
|
|
63
69
|
}
|
|
64
|
-
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Explicit provider id (mock is built in for smoke tests)\n --model <name> Explicit model name\n --session <id> Session id\n --config <path> Explicit config path (recorded, not auto-loaded)\n --resource <uri> Explicit resource URI (recorded, not auto-loaded)\n --extension <name> Explicit extension name (recorded, not auto-loaded)\n --tool <name> Explicit tool name (recorded, not auto-enabled)\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n --discover Enable workspace contribution discovery (opt-in)\n --discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)\n --no-discovery Disable discovery even if --discover is set\n --agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)\n --no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md\n --no-system-md Skip auto-loading the global SYSTEM.md layer\n --agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)\n --system-md-file <path> Read SYSTEM.md from <path> instead (source: user)\n -h, --help Show this help\n";
|
|
70
|
+
export declare const usage = "Usage: prism [--mode print|json|rpc] [-p prompt] [options]\n prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]\n\nOptions:\n -p, --prompt <text> Prompt to run in print/json mode\n --provider <name> Explicit provider id (mock is built in for smoke tests)\n --model <name> Explicit model name\n --session <id> Session id\n --config <path> Explicit config path (recorded, not auto-loaded)\n --resource <uri> Explicit resource URI (recorded, not auto-loaded)\n --extension <name> Explicit extension name (recorded, not auto-loaded)\n --tool <name> Explicit tool name (recorded, not auto-enabled)\n --system <text> System instructions\n --context <text> Context text\n --compact <entries> Auto-compaction threshold\n --max-tool-rounds <n> Maximum tool rounds\n --discover Enable workspace contribution discovery (opt-in)\n --discover-kinds <csv> Kinds to discover (default: skill; skill,tool,context,instructions)\n --no-discovery Disable discovery even if --discover is set\n --agents-config <path> App config root holding agents/<name>/AGENT.md bundles (opt-in)\n --no-agents-md Skip auto-loading <workspaceRoot>/AGENTS.md\n --no-system-md Skip auto-loading the global SYSTEM.md layer\n --agents-md-file <path> Read AGENTS.md from <path> instead (trust-gated, source: app)\n --system-md-file <path> Read SYSTEM.md from <path> instead (source: user)\n -h, --help Show this help\n";
|
|
65
71
|
export declare function parseCliArgs(argv: readonly string[]): CliOptions;
|
|
66
72
|
export declare function runCli(argv: readonly string[], runtime: CliRuntime): Promise<number>;
|
|
67
73
|
export declare function runPromptMode(session: AgentSession, options: CliOptions, stdout: Writable, mode: "print" | "json"): Promise<void>;
|
package/dist/cli-runner.js
CHANGED
|
@@ -10,7 +10,9 @@ import { registerDiscoveredInstructionInjectors } from "./node/instruction-injec
|
|
|
10
10
|
import { loadSystemPromptFiles } from "./node/system-project-prompts.js";
|
|
11
11
|
import { createPathTrustPolicy } from "./node/trust.js";
|
|
12
12
|
import { runRpcServer } from "./rpc.js";
|
|
13
|
+
import { initUsage, runInitCommand } from "./cli-init.js";
|
|
13
14
|
export const usage = `Usage: prism [--mode print|json|rpc] [-p prompt] [options]
|
|
15
|
+
prism init <dir> [--provider <name>] [--with-workflows] [--with-evals] [--force]
|
|
14
16
|
|
|
15
17
|
Options:
|
|
16
18
|
-p, --prompt <text> Prompt to run in print/json mode
|
|
@@ -167,6 +169,16 @@ function parseKinds(csv, flag) {
|
|
|
167
169
|
return kinds;
|
|
168
170
|
}
|
|
169
171
|
export async function runCli(argv, runtime) {
|
|
172
|
+
if (argv[0] === "init") {
|
|
173
|
+
const initRuntime = {
|
|
174
|
+
stdout: runtime.stdout,
|
|
175
|
+
stderr: runtime.stderr,
|
|
176
|
+
...(runtime.initTemplatesRoot !== undefined ? { templatesRoot: runtime.initTemplatesRoot } : {}),
|
|
177
|
+
...(runtime.initPackageVersion !== undefined ? { packageVersion: runtime.initPackageVersion } : {}),
|
|
178
|
+
...(runtime.cwd !== undefined ? { cwd: runtime.cwd } : {}),
|
|
179
|
+
};
|
|
180
|
+
return runInitCommand(argv.slice(1), initRuntime);
|
|
181
|
+
}
|
|
170
182
|
let options;
|
|
171
183
|
try {
|
|
172
184
|
options = parseCliArgs(argv);
|
|
@@ -176,7 +188,7 @@ export async function runCli(argv, runtime) {
|
|
|
176
188
|
return 2;
|
|
177
189
|
}
|
|
178
190
|
if (options.help) {
|
|
179
|
-
write(runtime.stdout, usage);
|
|
191
|
+
write(runtime.stdout, `${usage}\n${initUsage}`);
|
|
180
192
|
return 0;
|
|
181
193
|
}
|
|
182
194
|
try {
|
package/dist/content.d.ts
CHANGED
|
@@ -63,13 +63,30 @@ export interface MediaMimePolicy {
|
|
|
63
63
|
/** Reject when magic bytes disagree with declared media type. Default `true`. */
|
|
64
64
|
readonly strictMagicValidation?: boolean;
|
|
65
65
|
}
|
|
66
|
+
export interface MediaHostAddress {
|
|
67
|
+
readonly address: string;
|
|
68
|
+
readonly family: 4 | 6;
|
|
69
|
+
}
|
|
70
|
+
export type MediaHostnameResolver = (hostname: string, signal: AbortSignal) => Promise<readonly MediaHostAddress[]>;
|
|
71
|
+
export interface MediaUrlRequest {
|
|
72
|
+
readonly url: URL;
|
|
73
|
+
readonly address: MediaHostAddress;
|
|
74
|
+
readonly maxBytes: number;
|
|
75
|
+
readonly signal: AbortSignal;
|
|
76
|
+
}
|
|
77
|
+
export type MediaUrlRequester = (request: MediaUrlRequest) => Promise<Uint8Array>;
|
|
66
78
|
export interface ResolveMediaContentOptions {
|
|
67
79
|
readonly bounds?: MediaContentBounds;
|
|
68
80
|
readonly ssrf?: SsrfPolicy;
|
|
69
81
|
readonly mime?: MediaMimePolicy;
|
|
70
82
|
readonly loader?: ResourceLoader;
|
|
71
83
|
readonly loadContext?: ResourceLoadContext;
|
|
84
|
+
/** Trusted custom transport; the host owns DNS resolution and rebinding protection. */
|
|
72
85
|
readonly fetch?: typeof globalThis.fetch;
|
|
86
|
+
/** Test/custom resolver used only with the default or `requestUrl` pinned transport. */
|
|
87
|
+
readonly resolveHostname?: MediaHostnameResolver;
|
|
88
|
+
/** Trusted custom transport that must connect to `request.address`, not resolve `request.url` again. */
|
|
89
|
+
readonly requestUrl?: MediaUrlRequester;
|
|
73
90
|
readonly signal?: AbortSignal;
|
|
74
91
|
}
|
|
75
92
|
export interface ResolvedMediaContent {
|
|
@@ -99,4 +116,6 @@ export declare function assertSsrfAllowedUrl(url: string, policy?: SsrfPolicy):
|
|
|
99
116
|
export declare function sniffMediaMimeType(bytes: Uint8Array): string | undefined;
|
|
100
117
|
export declare function assertDeclaredMediaTypeMatches(declaredMediaType: string, bytes: Uint8Array, policy?: MediaMimePolicy): void;
|
|
101
118
|
export declare function resolveMediaContentBlock(block: MediaContentBlock, options?: ResolveMediaContentOptions): Promise<ResolvedMediaContent>;
|
|
119
|
+
/** Resolve and validate one complete media request before provider serialization/upload. */
|
|
120
|
+
export declare function resolveMediaContentBlocks(blocks: readonly MediaContentBlock[], options?: ResolveMediaContentOptions): Promise<readonly ResolvedMediaContent[]>;
|
|
102
121
|
export declare function loadBoundedBinaryResource(loader: ResourceLoader, uri: string, context: ResourceLoadContext | undefined, maxBytes: number, signal?: AbortSignal): Promise<Uint8Array>;
|