@malloy-publisher/create-malloy-package 0.0.6 → 0.0.7
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 -11
- package/dist/index.js +44 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,6 +135,10 @@ CLAUDE.md / AGENTS.md short, package-scoped agent instructions
|
|
|
135
135
|
.claude/skills/ the Malloy agent skills, copied in as real files
|
|
136
136
|
sales/ the package
|
|
137
137
|
publisher.json the manifest
|
|
138
|
+
malloy-config.json for the VS Code/Cursor Malloy extension, whose relative-path
|
|
139
|
+
resolution differs from Publisher's (machine-specific
|
|
140
|
+
absolute path; drop it and open the editor at sales/ instead
|
|
141
|
+
if you commit the package)
|
|
138
142
|
sales.malloy a starter model over the sample data
|
|
139
143
|
data/sales.csv the sample data
|
|
140
144
|
```
|
|
@@ -361,17 +365,32 @@ package's built-in DuckDB sandbox, so no database credentials are required.
|
|
|
361
365
|
|
|
362
366
|
### The workspace path
|
|
363
367
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
368
|
+
Spaces, apostrophes, and quotes in the workspace path are fine for querying data: the
|
|
369
|
+
model's data references are relative (`data/sales.csv`) and Publisher's per-package
|
|
370
|
+
DuckDB sandbox resolves them against the package's working directory, so the workspace
|
|
371
|
+
path never reaches DuckDB's path parser on that path. `~/Documents/My Projects`,
|
|
372
|
+
`~/Google Drive`, `~/OneDrive - Company`, and a `2026-08-13 Project Name` directory all
|
|
373
|
+
work — verified end-to-end (load and query a CSV) under paths containing a space, an
|
|
374
|
+
apostrophe, and a double quote. An earlier version of this tool refused all of them on a
|
|
375
|
+
premise that does not hold against the current server.
|
|
376
|
+
|
|
377
|
+
One server code path is an exception: the databases endpoint's schema probe builds an
|
|
378
|
+
absolute path literal and does reach the path parser, so a spaced/quoted workspace path
|
|
379
|
+
still loses row counts and column types there (the package still loads and queries
|
|
380
|
+
correctly). Pre-existing server behavior, tracked separately — not a reason to avoid
|
|
381
|
+
these paths.
|
|
382
|
+
|
|
383
|
+
What is refused is a path outside **printable ASCII** — an accent, an emoji, any
|
|
384
|
+
non-ASCII character, and every control character. This is the server's own rule, not
|
|
385
|
+
DuckDB's: Publisher checks an environment path against `[\x20-\x7E]` before mounting a
|
|
386
|
+
package from it, so a workspace under `~/josé` loads nothing. The server still reports
|
|
387
|
+
`serving`, with the environment missing and the reason only in the `loadErrors` of
|
|
388
|
+
`/api/v0/status`, so the scaffolder refuses up front instead — before anything is
|
|
389
|
+
written, naming the character. Control characters are refused for the additional reason
|
|
390
|
+
that they corrupt the commands and briefing files the scaffold writes verbatim.
|
|
391
|
+
|
|
392
|
+
So a home directory whose username carries an accent needs the workspace somewhere
|
|
393
|
+
else, such as `~/malloy-workspace`.
|
|
375
394
|
|
|
376
395
|
## License
|
|
377
396
|
|
package/dist/index.js
CHANGED
|
@@ -665,14 +665,26 @@ var MCP_PORT = 4040;
|
|
|
665
665
|
var ALT_PUBLISHER_PORT = PUBLISHER_PORT + 100;
|
|
666
666
|
var ALT_MCP_PORT = MCP_PORT + 100;
|
|
667
667
|
var BIND_HOST = "127.0.0.1";
|
|
668
|
-
var SERVER_VERSION = "0.0.
|
|
668
|
+
var SERVER_VERSION = "0.0.244";
|
|
669
669
|
function startCommandFor(envName) {
|
|
670
670
|
return `npx -y @malloy-publisher/server@${SERVER_VERSION} --server_root . ` + `--config ./publisher.config.json --host ${BIND_HOST} ` + `--watch-env ${envName}`;
|
|
671
671
|
}
|
|
672
672
|
function resetCommandFor(envName) {
|
|
673
673
|
return `${startCommandFor(envName)} --init`;
|
|
674
674
|
}
|
|
675
|
-
|
|
675
|
+
function portOverrideCommandFor(result) {
|
|
676
|
+
return withAlternatePorts(result.hasStartScript ? "npm start" : result.startCommand, result.hasStartScript);
|
|
677
|
+
}
|
|
678
|
+
function resetPortOverrideCommandFor(result) {
|
|
679
|
+
return withAlternatePorts(result.hasResetScript ? "npm run reset" : result.resetCommand, result.hasResetScript);
|
|
680
|
+
}
|
|
681
|
+
function withAlternatePorts(command, viaNpmScript) {
|
|
682
|
+
return `${command}${viaNpmScript ? " --" : ""} ` + `--port ${ALT_PUBLISHER_PORT} --mcp_port ${ALT_MCP_PORT}`;
|
|
683
|
+
}
|
|
684
|
+
function isServablePathCharacter(character) {
|
|
685
|
+
const codePoint = character.codePointAt(0);
|
|
686
|
+
return codePoint >= 32 && codePoint <= 126;
|
|
687
|
+
}
|
|
676
688
|
var RESERVED_PACKAGE_NAMES = new Set([
|
|
677
689
|
"AGENTS.md",
|
|
678
690
|
MALLOY_AGENTS_FILE,
|
|
@@ -770,6 +782,15 @@ function createPackage(options, result) {
|
|
|
770
782
|
assertWithinWorkspace(dataDir, options.cwd, `${name}/data`);
|
|
771
783
|
fs4.mkdirSync(dataDir, { recursive: true });
|
|
772
784
|
writeFile(path3.join(packageDir, "publisher.json"), JSON.stringify({ name }, null, 2) + `
|
|
785
|
+
`, options.cwd);
|
|
786
|
+
writeFile(path3.join(packageDir, "malloy-config.json"), JSON.stringify({
|
|
787
|
+
connections: {
|
|
788
|
+
duckdb: {
|
|
789
|
+
is: "duckdb",
|
|
790
|
+
workingDirectory: path3.resolve(packageDir)
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}, null, 2) + `
|
|
773
794
|
`, options.cwd);
|
|
774
795
|
let dataPath;
|
|
775
796
|
if (options.dataFile !== undefined) {
|
|
@@ -803,7 +824,7 @@ function createPackage(options, result) {
|
|
|
803
824
|
function forceDescription(name, modelFile, host) {
|
|
804
825
|
const agentFiles = host === "cursor" ? "AGENTS.md" : "AGENTS.md and CLAUDE.md";
|
|
805
826
|
const mcpPath = mcpConfigPathFor(host);
|
|
806
|
-
return `--force does not empty the directory. It rewrites ${name}/publisher.json, ` + `${name}/${modelFile} and the data file it copies into ${name}/data/, and
|
|
827
|
+
return `--force does not empty the directory. It rewrites ${name}/publisher.json, ` + `${name}/malloy-config.json, ${name}/${modelFile} and the data file it ` + `copies into ${name}/data/, and leaves anything else in there alone. ` + `It also refreshes .claude/skills/ ` + `from the bundled copies, as every run does. Outside the package it ` + `replaces ${agentFiles}; in package.json it sets only the "start" and ` + `"reset" scripts and keeps the rest of the file as it is; in ${mcpPath} it ` + `sets only the "malloy" server and keeps the others. publisher.config.json ` + `is extended, never rewritten, and .gitignore only gains the lines it is ` + `missing, with or without --force. None of those merges is a rewrite even ` + `when the file cannot be read: a package.json that is not valid JSON aborts ` + `the run before anything is written, and an unreadable ${mcpPath} or ` + `.gitignore is left alone and reported. If this directory has an AGENTS.md ` + `of its own, --force is not what you want for it: a run without --force ` + `keeps that file and writes the Publisher briefing to ${MALLOY_AGENTS_FILE} ` + `beside it instead, regenerating that one on every run.`;
|
|
807
828
|
}
|
|
808
829
|
function mcpConfigPathFor(host) {
|
|
809
830
|
return host === "cursor" ? ".cursor/mcp.json" : ".mcp.json";
|
|
@@ -907,24 +928,23 @@ function installWorkspaceSkills(cwd, result) {
|
|
|
907
928
|
}
|
|
908
929
|
function assertServablePath(cwd) {
|
|
909
930
|
const absolute = path3.resolve(cwd);
|
|
910
|
-
const
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
931
|
+
for (const character of absolute) {
|
|
932
|
+
if (!isServablePathCharacter(character)) {
|
|
933
|
+
const codePoint = character.codePointAt(0);
|
|
934
|
+
const consequence = codePoint < 32 || codePoint === 127 ? `which would corrupt the commands and briefing files this ` + `tool writes verbatim` : `and Publisher refuses to mount a package from a path ` + `outside printable ASCII: it would report serving with this ` + `workspace's environment missing, naming the reason only in ` + `/api/v0/status loadErrors`;
|
|
935
|
+
throw new ScaffoldError(`This workspace cannot live in ${printable(absolute)}: its path ` + `contains ${describeCharacter(character)}, ${consequence}. ` + `Move to a path of printable ASCII and run again — spaces are ` + `fine, so ~/Documents/My Projects works.`);
|
|
914
936
|
}
|
|
915
937
|
}
|
|
916
938
|
}
|
|
917
939
|
function describeCharacter(character) {
|
|
918
940
|
const named = {
|
|
919
|
-
"
|
|
920
|
-
"
|
|
921
|
-
|
|
922
|
-
"\\": "a backslash",
|
|
923
|
-
"\t": "a tab"
|
|
941
|
+
"\t": "a tab",
|
|
942
|
+
"\n": "a line feed",
|
|
943
|
+
"\r": "a carriage return"
|
|
924
944
|
};
|
|
925
|
-
const label = named[character] ?? `"${character}"`;
|
|
945
|
+
const label = named[character] ?? `"${printable(character)}"`;
|
|
926
946
|
const codePoint = character.codePointAt(0);
|
|
927
|
-
return
|
|
947
|
+
return `${label} (U+${codePoint.toString(16).toUpperCase().padStart(4, "0")})`;
|
|
928
948
|
}
|
|
929
949
|
function assertWorkspacePathsContained(options, mcpConfigPath) {
|
|
930
950
|
const relatives = [
|
|
@@ -1347,7 +1367,7 @@ function renderAgentsFile(result, host, envPackages) {
|
|
|
1347
1367
|
const skillsCount = String(result.skillsInstalled);
|
|
1348
1368
|
const skillsNote = result.skillsInstalled === 0 ? "This run installed no skills into `.claude/skills/` (the scaffolder's output says why), so there are none here to load. Pull the same guidance as MCP prompts from the endpoint above instead." : host === "cursor" ? `\`.claude/skills/\` holds ${skillsCount} Malloy agent skills as real files. Cursor reads \`AGENTS.md\`; these skills are the same guidance broken out by task, and any MCP client can also pull them as prompts from the endpoint above.` : `\`.claude/skills/\` holds ${skillsCount} Malloy agent skills as real files, so Claude Code auto-discovers them. Hosts that read \`AGENTS.md\` rather than Anthropic Agent Skills can pull the same guidance as MCP prompts from the endpoint above.`;
|
|
1349
1369
|
const startCommand = result.hasStartScript ? "npm start" : result.startCommand;
|
|
1350
|
-
const portOverrideCommand =
|
|
1370
|
+
const portOverrideCommand = portOverrideCommandFor(result);
|
|
1351
1371
|
return renderTemplate("AGENTS.md", {
|
|
1352
1372
|
title: result.packageCreated ? `${result.packageName}: a Malloy Publisher package` : "A Malloy Publisher workspace",
|
|
1353
1373
|
startCommand,
|
|
@@ -1378,7 +1398,7 @@ function packageSection(result, envPackages) {
|
|
|
1378
1398
|
];
|
|
1379
1399
|
if (result.packageCreated) {
|
|
1380
1400
|
const base = restBase(result.packageName);
|
|
1381
|
-
lines.push(`\`${result.packageName}/${result.modelFile}\` defines a Malloy source named \`${result.sourceName}\` over local data. Read it for the real source, field, and view names and use them verbatim; never guess them. The package's REST base is:`, "", "```", base, "```", "", "Run one of its views from a script:", "", "```bash", `curl -s -X POST ${base}/models/${result.modelFile}/query \\`, " -H 'content-type: application/json' \\", ` -d '{"query":"run: ${result.sourceName} -> overview"}'`, "```", "");
|
|
1401
|
+
lines.push(`\`${result.packageName}/${result.modelFile}\` defines a Malloy source named \`${result.sourceName}\` over local data. Read it for the real source, field, and view names and use them verbatim; never guess them. The package's REST base is:`, "", "```", base, "```", "", "Run one of its views from a script:", "", "```bash", `curl -s -X POST ${base}/models/${result.modelFile}/query \\`, " -H 'content-type: application/json' \\", ` -d '{"query":"run: ${result.sourceName} -> overview"}'`, "```", "", `The VS Code / Cursor Malloy extension resolves the model's relative \`duckdb.table('data/…')\` paths against the EDITOR WORKSPACE root, while Publisher resolves them against the package directory. \`${result.packageName}/malloy-config.json\` (generated, machine-specific absolute path) bridges that for an editor opened at this workspace root; without it, open the editor at \`${result.packageName}/\` itself, or a model Publisher serves fine shows unresolved-table errors in the editor.`, "");
|
|
1382
1402
|
}
|
|
1383
1403
|
if (others.length > 0) {
|
|
1384
1404
|
lines.push(otherPackagesParagraph(result, others, restBase), "");
|
|
@@ -1423,7 +1443,8 @@ function workspaceGitignoreEntries() {
|
|
|
1423
1443
|
"publisher_data/",
|
|
1424
1444
|
"publisher.db*",
|
|
1425
1445
|
"*.log",
|
|
1426
|
-
".DS_Store"
|
|
1446
|
+
".DS_Store",
|
|
1447
|
+
"malloy-config.json"
|
|
1427
1448
|
];
|
|
1428
1449
|
}
|
|
1429
1450
|
function workspaceGitignore() {
|
|
@@ -1984,6 +2005,12 @@ function formatSuccess(result) {
|
|
|
1984
2005
|
if (!result.hasStartScript) {
|
|
1985
2006
|
lines.push(...exposureWarning("npm start", result.declinedStartScript));
|
|
1986
2007
|
}
|
|
2008
|
+
lines.push(dim(` If port ${result.publisherPort} is taken by a Publisher serving a ` + `DIFFERENT
|
|
2009
|
+
workspace, boot with
|
|
2010
|
+
` + ` ${result.needsReset ? resetPortOverrideCommandFor(result) : portOverrideCommandFor(result)}
|
|
2011
|
+
` + ` and edit the url in ${result.mcpConfigPath} to the new MCP port.` + (result.needsReset ? "" : `
|
|
2012
|
+
One already serving THIS workspace is the other case: ` + `stop it,
|
|
2013
|
+
because the lock is on the workspace, not the ` + `port.`)));
|
|
1987
2014
|
lines.push(` ${cyan(url)} ${dim("explore in the browser")}`);
|
|
1988
2015
|
lines.push("");
|
|
1989
2016
|
lines.push(bold("Check it is ready:"));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@malloy-publisher/create-malloy-package",
|
|
3
3
|
"description": "Scaffold a Malloy Publisher package and a local agent workspace, so one command takes you from nothing to an agent that can query your data.",
|
|
4
|
-
"version": "0.0.
|
|
4
|
+
"version": "0.0.7",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|