artifacty 0.1.0 → 0.1.1
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 +128 -33
- package/docs/artifact-schema-v1.md +26 -1
- package/docs/integrations.md +66 -8
- package/docs/network-sharing.md +41 -0
- package/docs/release-checklist.md +13 -0
- package/docs/sarif-csv-artifact-plan.md +65 -0
- package/package.json +15 -3
- package/scripts/smoke.sh +2 -1
- package/src/cli.js +30 -8
- package/src/client/editor.js +35 -2
- package/src/client/viewer.js +67 -0
- package/src/lib/converters.js +448 -10
- package/src/lib/editor-assets.js +45 -2
- package/src/lib/installer.js +13 -4
- package/src/lib/render.js +348 -8
- package/src/lib/storage.js +60 -4
- package/src/lib/token.js +25 -0
- package/src/mcp-server.js +8 -6
- package/src/server.js +104 -25
package/src/cli.js
CHANGED
|
@@ -18,6 +18,7 @@ import { checkMcpTools } from "./lib/check.js";
|
|
|
18
18
|
import { installAgent } from "./lib/installer.js";
|
|
19
19
|
import { serviceCommand } from "./lib/service.js";
|
|
20
20
|
import { resolvePublicBaseUrl } from "./lib/server-state.js";
|
|
21
|
+
import { generateToken } from "./lib/token.js";
|
|
21
22
|
import { startServer } from "./server.js";
|
|
22
23
|
|
|
23
24
|
const PACKAGE_ROOT = path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
@@ -32,17 +33,37 @@ async function main() {
|
|
|
32
33
|
return;
|
|
33
34
|
}
|
|
34
35
|
|
|
36
|
+
if (command === "token" || command === "generate-token") {
|
|
37
|
+
const token = generateToken(options);
|
|
38
|
+
if (options.raw) {
|
|
39
|
+
process.stdout.write(`${token.token}\n`);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
printJson(token);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
if (command === "serve") {
|
|
47
|
+
if (options.generateToken && options.apiToken) {
|
|
48
|
+
throw new Error("Use either --api-token or --generate-token, not both");
|
|
49
|
+
}
|
|
50
|
+
const generatedToken = options.generateToken ? generateToken(options) : null;
|
|
36
51
|
const server = await startServer({
|
|
37
52
|
host: options.host,
|
|
38
53
|
port: options.port,
|
|
39
54
|
home: options.home,
|
|
40
|
-
apiToken: options.apiToken,
|
|
55
|
+
apiToken: generatedToken?.token || options.apiToken,
|
|
41
56
|
shareMode: options.shareMode,
|
|
42
57
|
allowSecrets: options.allowSecrets
|
|
43
58
|
});
|
|
44
59
|
process.stderr.write(`Artifacty listening on ${server.url}\n`);
|
|
45
60
|
process.stderr.write(`Store: ${server.store.home}\n`);
|
|
61
|
+
if (generatedToken) {
|
|
62
|
+
process.stderr.write(`API token: ${generatedToken.token}\n`);
|
|
63
|
+
process.stderr.write(`HTTP header: ${generatedToken.header}\n`);
|
|
64
|
+
process.stderr.write(`Create URL: ${server.url}/new?token=${encodeURIComponent(generatedToken.token)}\n`);
|
|
65
|
+
process.stderr.write(`Import URL: ${server.url}/import?token=${encodeURIComponent(generatedToken.token)}\n`);
|
|
66
|
+
}
|
|
46
67
|
return;
|
|
47
68
|
}
|
|
48
69
|
|
|
@@ -235,7 +256,7 @@ function parseArgs(args) {
|
|
|
235
256
|
}
|
|
236
257
|
|
|
237
258
|
const key = arg.slice(2);
|
|
238
|
-
if (key === "raw" || key === "dry-run" || key === "trust" || key === "include-archived" || key === "allow-secrets") {
|
|
259
|
+
if (key === "raw" || key === "dry-run" || key === "trust" || key === "include-archived" || key === "allow-secrets" || key === "generate-token") {
|
|
239
260
|
options[toCamelCase(key)] = true;
|
|
240
261
|
continue;
|
|
241
262
|
}
|
|
@@ -247,7 +268,7 @@ function parseArgs(args) {
|
|
|
247
268
|
|
|
248
269
|
if (key === "tag") {
|
|
249
270
|
options.tag = [...(options.tag || []), value];
|
|
250
|
-
} else if (key === "port" || key === "limit" || key === "version" || key === "schema-version" || key === "timeout") {
|
|
271
|
+
} else if (key === "port" || key === "limit" || key === "version" || key === "schema-version" || key === "timeout" || key === "bytes") {
|
|
251
272
|
options[toCamelCase(key)] = Number(value);
|
|
252
273
|
} else {
|
|
253
274
|
options[toCamelCase(key)] = value;
|
|
@@ -292,12 +313,13 @@ function printHelp() {
|
|
|
292
313
|
process.stdout.write(`Artifacty
|
|
293
314
|
|
|
294
315
|
Usage:
|
|
295
|
-
artifacty
|
|
296
|
-
artifacty
|
|
297
|
-
artifacty
|
|
298
|
-
artifacty
|
|
316
|
+
artifacty token [--bytes 32] [--raw]
|
|
317
|
+
artifacty serve [--host 127.0.0.1] [--port 8787] [--home ~/.artifacty] [--generate-token] [--bytes 32]
|
|
318
|
+
artifacty publish --title <title> (--file <path> | --content <text>) [--format html|markdown|text|json|code|svg|mermaid|react] [--source agent] [--tag tag]
|
|
319
|
+
artifacty import --agent claude|codex|gemini|auto (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react] [--tag tag]
|
|
320
|
+
artifacty install claude|codex|gemini|all [--dry-run] [--config <path>] [--server-path <path>] [--url http://127.0.0.1:8787] [--timeout 30000]
|
|
299
321
|
artifacty check [--server-path <path>] [--timeout 5000]
|
|
300
|
-
artifacty update <id> (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json]
|
|
322
|
+
artifacty update <id> (--file <path> | --content <text>) [--title <title>] [--format html|markdown|text|json|code|svg|mermaid|react]
|
|
301
323
|
artifacty archive <id>
|
|
302
324
|
artifacty restore <id>
|
|
303
325
|
artifacty audit [--artifact <id>] [--limit 100]
|
package/src/client/editor.js
CHANGED
|
@@ -15,6 +15,8 @@ const messages = {
|
|
|
15
15
|
...(globalThis.ARTIFACTY_I18N || {})
|
|
16
16
|
};
|
|
17
17
|
|
|
18
|
+
const SUPPORTED_FORMATS = ["markdown", "html", "json", "text", "code", "svg", "mermaid", "react"];
|
|
19
|
+
|
|
18
20
|
const editorTheme = EditorView.theme({
|
|
19
21
|
"&": {
|
|
20
22
|
minHeight: "52vh",
|
|
@@ -146,6 +148,16 @@ function enhanceTextarea(textarea) {
|
|
|
146
148
|
return;
|
|
147
149
|
}
|
|
148
150
|
|
|
151
|
+
if (format === "svg") {
|
|
152
|
+
const frame = document.createElement("iframe");
|
|
153
|
+
frame.className = "editor-preview-frame";
|
|
154
|
+
frame.setAttribute("sandbox", "");
|
|
155
|
+
frame.srcdoc = content;
|
|
156
|
+
preview.append(frame);
|
|
157
|
+
status.textContent = formatLabel(format);
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
|
|
149
161
|
if (format === "json") {
|
|
150
162
|
const pre = document.createElement("pre");
|
|
151
163
|
try {
|
|
@@ -176,7 +188,7 @@ function enhanceTextarea(textarea) {
|
|
|
176
188
|
}
|
|
177
189
|
|
|
178
190
|
function languageExtension(format) {
|
|
179
|
-
if (format === "html") {
|
|
191
|
+
if (format === "html" || format === "svg") {
|
|
180
192
|
return html();
|
|
181
193
|
}
|
|
182
194
|
if (format === "json") {
|
|
@@ -189,7 +201,7 @@ function languageExtension(format) {
|
|
|
189
201
|
}
|
|
190
202
|
|
|
191
203
|
function detectFormat({ explicit, fileName, content }) {
|
|
192
|
-
if (
|
|
204
|
+
if (SUPPORTED_FORMATS.includes(explicit)) {
|
|
193
205
|
return explicit;
|
|
194
206
|
}
|
|
195
207
|
|
|
@@ -203,8 +215,29 @@ function detectFormat({ explicit, fileName, content }) {
|
|
|
203
215
|
if (lowerName.endsWith(".json")) {
|
|
204
216
|
return "json";
|
|
205
217
|
}
|
|
218
|
+
if (lowerName.endsWith(".svg")) {
|
|
219
|
+
return "svg";
|
|
220
|
+
}
|
|
221
|
+
if (lowerName.endsWith(".mmd") || lowerName.endsWith(".mermaid")) {
|
|
222
|
+
return "mermaid";
|
|
223
|
+
}
|
|
224
|
+
if (lowerName.endsWith(".jsx") || lowerName.endsWith(".tsx")) {
|
|
225
|
+
return "react";
|
|
226
|
+
}
|
|
227
|
+
if (/\.(js|ts|py|rb|go|rs|java|c|cc|cpp|cs|php|swift|kt|sh|bash|zsh)$/.test(lowerName)) {
|
|
228
|
+
return "code";
|
|
229
|
+
}
|
|
206
230
|
|
|
207
231
|
const trimmed = String(content || "").trimStart();
|
|
232
|
+
if (/^(?:<\?xml[\s\S]*?\?>\s*)?<svg[\s>]/i.test(trimmed)) {
|
|
233
|
+
return "svg";
|
|
234
|
+
}
|
|
235
|
+
if (/^(graph|flowchart|sequenceDiagram|classDiagram|stateDiagram|stateDiagram-v2|erDiagram|gantt|pie|mindmap|journey)\b/m.test(trimmed)) {
|
|
236
|
+
return "mermaid";
|
|
237
|
+
}
|
|
238
|
+
if (/\b(import\s+React|from\s+['"]react['"]|export\s+default\s+function|export\s+default\s+\()/m.test(trimmed) || /<[A-Z][A-Za-z0-9]*[\s/>]/.test(trimmed)) {
|
|
239
|
+
return "react";
|
|
240
|
+
}
|
|
208
241
|
if (trimmed.startsWith("<!doctype") || trimmed.startsWith("<html") || trimmed.startsWith("<")) {
|
|
209
242
|
return "html";
|
|
210
243
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { EditorView, basicSetup } from "codemirror";
|
|
2
|
+
import { EditorState } from "@codemirror/state";
|
|
3
|
+
import { javascript } from "@codemirror/lang-javascript";
|
|
4
|
+
import { html } from "@codemirror/lang-html";
|
|
5
|
+
import { json } from "@codemirror/lang-json";
|
|
6
|
+
import { markdown } from "@codemirror/lang-markdown";
|
|
7
|
+
|
|
8
|
+
const viewerTheme = EditorView.theme({
|
|
9
|
+
"&": {
|
|
10
|
+
minHeight: "50vh"
|
|
11
|
+
},
|
|
12
|
+
".cm-content": {
|
|
13
|
+
minHeight: "50vh"
|
|
14
|
+
},
|
|
15
|
+
".cm-scroller": {
|
|
16
|
+
overflow: "auto"
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
for (const container of document.querySelectorAll("[data-artifacty-code-viewer]")) {
|
|
21
|
+
enhanceCodeViewer(container);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function enhanceCodeViewer(container) {
|
|
25
|
+
const source = container.querySelector("textarea")?.value || "";
|
|
26
|
+
const fallback = container.querySelector(".artifact-code-fallback");
|
|
27
|
+
const language = container.dataset.language || "";
|
|
28
|
+
|
|
29
|
+
const mount = document.createElement("div");
|
|
30
|
+
mount.className = "artifact-codemirror-mount";
|
|
31
|
+
container.append(mount);
|
|
32
|
+
|
|
33
|
+
new EditorView({
|
|
34
|
+
doc: source,
|
|
35
|
+
parent: mount,
|
|
36
|
+
extensions: [
|
|
37
|
+
basicSetup,
|
|
38
|
+
EditorState.readOnly.of(true),
|
|
39
|
+
EditorView.editable.of(false),
|
|
40
|
+
EditorView.lineWrapping,
|
|
41
|
+
viewerTheme,
|
|
42
|
+
languageExtension(language)
|
|
43
|
+
]
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
fallback?.remove();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function languageExtension(language) {
|
|
50
|
+
const normalized = String(language || "").trim().toLowerCase();
|
|
51
|
+
if (["js", "javascript", "jsx"].includes(normalized)) {
|
|
52
|
+
return javascript({ jsx: true });
|
|
53
|
+
}
|
|
54
|
+
if (["ts", "typescript", "tsx"].includes(normalized)) {
|
|
55
|
+
return javascript({ typescript: true, jsx: normalized === "tsx" });
|
|
56
|
+
}
|
|
57
|
+
if (["html", "xml", "svg"].includes(normalized)) {
|
|
58
|
+
return html();
|
|
59
|
+
}
|
|
60
|
+
if (normalized === "json") {
|
|
61
|
+
return json();
|
|
62
|
+
}
|
|
63
|
+
if (["md", "markdown"].includes(normalized)) {
|
|
64
|
+
return markdown();
|
|
65
|
+
}
|
|
66
|
+
return [];
|
|
67
|
+
}
|