@arthovlabs/liber 0.0.0-stage → 0.5.0
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/LICENSE +21 -0
- package/README.md +348 -2
- package/bin/liber +706 -0
- package/bin/liber-tui +244 -0
- package/bin/liber-tui.py +825 -0
- package/bin/liberd-entry +17 -0
- package/liber.yaml +84 -0
- package/package.json +43 -4
- package/resources/icons/appIcon.png +0 -0
- package/resources/icons/appIcon.svg +20 -0
- package/resources/icons/appIcon@512.png +0 -0
- package/resources/icons/atom-transparent-256.png +0 -0
- package/resources/icons/atom-transparent.png +0 -0
- package/resources/icons/atom-transparent.svg +19 -0
- package/resources/icons/make-atom.js +168 -0
- package/src/cli/ui.js +520 -0
- package/src/daemon/core/auth.js +217 -0
- package/src/daemon/core/automations.js +130 -0
- package/src/daemon/core/budget.js +145 -0
- package/src/daemon/core/circuit-breaker.js +31 -0
- package/src/daemon/core/config-apply.js +223 -0
- package/src/daemon/core/connectors.js +131 -0
- package/src/daemon/core/engine.js +1029 -0
- package/src/daemon/core/evidence.js +306 -0
- package/src/daemon/core/executor.js +10 -0
- package/src/daemon/core/grounding.js +420 -0
- package/src/daemon/core/injection.js +24 -0
- package/src/daemon/core/journal.js +81 -0
- package/src/daemon/core/liber-soul.js +157 -0
- package/src/daemon/core/lock.js +32 -0
- package/src/daemon/core/memory.js +106 -0
- package/src/daemon/core/model-switch.js +58 -0
- package/src/daemon/core/policy.js +102 -0
- package/src/daemon/core/providers.js +503 -0
- package/src/daemon/core/registry.js +356 -0
- package/src/daemon/core/secrets.js +35 -0
- package/src/daemon/core/skills.js +264 -0
- package/src/daemon/core/tools/browser-tool.js +280 -0
- package/src/daemon/core/tools/file-tools.js +277 -0
- package/src/daemon/core/tools/memory-tool.js +127 -0
- package/src/daemon/core/tools/skill-tool.js +44 -0
- package/src/daemon/core/tools/task-tool.js +41 -0
- package/src/daemon/core/tools/terminal-tool.js +449 -0
- package/src/daemon/core/tools/todo-tool.js +100 -0
- package/src/daemon/core/tools/web-tool.js +181 -0
- package/src/daemon/core/trace.js +40 -0
- package/src/daemon/core/validate.js +105 -0
- package/src/daemon/core/vault.js +158 -0
- package/src/daemon/core/verifier.js +98 -0
- package/src/daemon/core/voice.js +83 -0
- package/src/daemon/core/workspace.js +199 -0
- package/src/daemon/liberd.js +1343 -0
- package/src/daemon/llm/adapters/anthropic-adapter.js +190 -0
- package/src/daemon/llm/adapters/gemini-adapter.js +148 -0
- package/src/daemon/llm/adapters/ollama-adapter.js +74 -0
- package/src/daemon/llm/adapters/openai-adapter.js +148 -0
- package/src/daemon/llm/base.js +31 -0
- package/src/daemon/llm/manager.js +134 -0
- package/src/utils/config-loader.js +109 -0
- package/src/utils/logger.js +56 -0
package/bin/liberd-entry
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// bin/liberd-entry
|
|
3
|
+
const liberd = require('../src/daemon/liberd');
|
|
4
|
+
|
|
5
|
+
// Handle termination signals for clean shutdown
|
|
6
|
+
process.on('SIGINT', () => liberd.stop());
|
|
7
|
+
process.on('SIGTERM', () => liberd.stop());
|
|
8
|
+
|
|
9
|
+
// Safety net: a bug in one request must never kill the daemon silently.
|
|
10
|
+
process.on('uncaughtException', (err) => {
|
|
11
|
+
console.error(`[liberd] uncaughtException (daemon stays up): ${err && err.message}`);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
liberd.start().catch(err => {
|
|
15
|
+
console.error(`[FATAL] Daemon crashed: ${err.message}`);
|
|
16
|
+
process.exit(1);
|
|
17
|
+
});
|
package/liber.yaml
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# liber.yaml
|
|
2
|
+
# Manifest Configuration Liber - Production Cloud Version
|
|
3
|
+
|
|
4
|
+
agent:
|
|
5
|
+
name: "dev-bot"
|
|
6
|
+
version: "2.1.0"
|
|
7
|
+
system_prompt: "You are the Liber Agent. You are an autonomous, precision-focused AI software engineer and system orchestrator. You have full access to tools: read_file, write_file, patch, search_files, terminal, fetch_url, todo_list, memory, and skill. Always think structurally before acting, use patch for surgical code modifications, and verify every step of your work. Always respond, plan, and reason in English."
|
|
8
|
+
|
|
9
|
+
engine:
|
|
10
|
+
# === Provider switching ==================================================
|
|
11
|
+
# Any OpenAI-compatible API works with provider: openai + endpoint + model.
|
|
12
|
+
# OpenAI endpoint: https://api.openai.com/v1 key: OPENAI_API_KEY
|
|
13
|
+
# Groq endpoint: https://api.groq.com/openai/v1 key: OPENAI_API_KEY
|
|
14
|
+
# Mistral endpoint: https://api.mistral.ai/v1 key: OPENAI_API_KEY
|
|
15
|
+
# DeepSeek endpoint: https://api.deepseek.com/v1 key: OPENAI_API_KEY
|
|
16
|
+
# OpenRouter endpoint: https://openrouter.ai/api/v1 key: OPENAI_API_KEY
|
|
17
|
+
# xAI endpoint: https://api.x.ai/v1 key: OPENAI_API_KEY
|
|
18
|
+
# Together endpoint: https://api.together.xyz/v1 key: OPENAI_API_KEY
|
|
19
|
+
# Dedicated adapters (native API translation, streaming included):
|
|
20
|
+
# Anthropic provider: anthropic endpoint: https://api.anthropic.com
|
|
21
|
+
# key: ANTHROPIC_API_KEY · model: claude-sonnet-4-5
|
|
22
|
+
# Google provider: gemini endpoint: https://generativelanguage.googleapis.com
|
|
23
|
+
# key: GEMINI_API_KEY · model: gemini-2.5-pro
|
|
24
|
+
# Put the key in .env (or your environment) — never in this file.
|
|
25
|
+
provider: "openai" # LLMManager adapter id: openai | anthropic | gemini | ollama
|
|
26
|
+
model: "deepseek-v4.1-flash" # Active cloud model
|
|
27
|
+
endpoint: "https://ollama.com/v1" # Ollama Cloud, OpenAI-compatible
|
|
28
|
+
apiKey: "${OLLAMA_CLOUD_API_KEY}" # Loaded automatically from .env or environment
|
|
29
|
+
temperature: 0.2
|
|
30
|
+
max_tokens: 4096
|
|
31
|
+
top_p: 1
|
|
32
|
+
reasoning_effort: "auto" # auto | low | medium | high — forwarded when the provider supports it
|
|
33
|
+
stream: true # stream model output as DRAFT (unverified) while the audit runs
|
|
34
|
+
native_tools: true # use the provider's tool-calling API; falls back to the text protocol on error
|
|
35
|
+
retry_attempts: 3 # transient provider errors (429/5xx/timeout) are retried with backoff
|
|
36
|
+
retry_base_ms: 700
|
|
37
|
+
# price_in_per_1k: 0.0 # set both to surface a cost_estimate in turn telemetry
|
|
38
|
+
# price_out_per_1k: 0.0
|
|
39
|
+
fallback_models: ["glm-5.3-flash"]
|
|
40
|
+
|
|
41
|
+
limits:
|
|
42
|
+
max_cost_per_day: 1
|
|
43
|
+
max_tokens_per_day: 0
|
|
44
|
+
circuit_breaker:
|
|
45
|
+
max_loops: 12
|
|
46
|
+
timeout: 30s
|
|
47
|
+
|
|
48
|
+
# Zero-hallucination enforcement (see src/daemon/core/grounding.js)
|
|
49
|
+
grounding:
|
|
50
|
+
enabled: true
|
|
51
|
+
max_repairs: 2 # audit-repair rounds before an answer ships with a GROUNDING AUDIT warning
|
|
52
|
+
require_readback: true # write_file/patch must be followed by read_file before success is claimed
|
|
53
|
+
require_citations: true # every audited claim must cite an [E#] receipt
|
|
54
|
+
semantic_verifier: true # second pass: does the cited receipt actually entail the claim?
|
|
55
|
+
doctrine_budget_chars: 7000
|
|
56
|
+
|
|
57
|
+
# Procedural skill library (see src/daemon/core/skills.js)
|
|
58
|
+
skills:
|
|
59
|
+
enabled: true
|
|
60
|
+
budget_chars: 7000 # index + always-on bodies + auto-selected bodies
|
|
61
|
+
project_dir: ".liber/skills"
|
|
62
|
+
|
|
63
|
+
# Tool permission policy (file tools only; the shell is not sandboxed)
|
|
64
|
+
policy:
|
|
65
|
+
write_roots: ["."] # relative to the daemon's working directory
|
|
66
|
+
allow_outside_workspace: false # true allows write_file/patch outside write_roots
|
|
67
|
+
terminal:
|
|
68
|
+
# powershell (Constrained Language Mode) or docker (real container: no network,
|
|
69
|
+
# read-only root, workspace-mounted only). Docker needs a running daemon.
|
|
70
|
+
backend: "docker"
|
|
71
|
+
image: "alpine:latest"
|
|
72
|
+
allow_network: false
|
|
73
|
+
mode: "allowlist" # read_only: block state-changing commands · allowlist: only the list · prompt: HITL only
|
|
74
|
+
allowlist: ["npm test", "node --test", "git status", "git diff", "git add", "git commit", "git push"]
|
|
75
|
+
denylist: []
|
|
76
|
+
|
|
77
|
+
memory:
|
|
78
|
+
strategy: "sliding-window"
|
|
79
|
+
window_size: 20
|
|
80
|
+
persist: true
|
|
81
|
+
path: ".liber/session.json"
|
|
82
|
+
|
|
83
|
+
telemetry:
|
|
84
|
+
trace: true # append one JSONL record per completed turn to .liber/traces/
|
package/package.json
CHANGED
|
@@ -1,6 +1,45 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arthovlabs/liber",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "LiBER — autonomous agent orchestrator with a machine-enforced grounding gate and a skill library",
|
|
5
|
+
"bin": {
|
|
6
|
+
"liber": "bin/liber",
|
|
7
|
+
"liberd": "bin/liberd-entry"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --test",
|
|
11
|
+
"start": "node bin/liber",
|
|
12
|
+
"daemon": "node bin/liberd-entry"
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"blessed": "^0.1.81",
|
|
16
|
+
"commander": "^15.0.0",
|
|
17
|
+
"js-yaml": "^5.5.0"
|
|
18
|
+
},
|
|
19
|
+
"publishConfig": {
|
|
20
|
+
"access": "public"
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"bin/liber",
|
|
24
|
+
"bin/liberd-entry",
|
|
25
|
+
"bin/liber-tui",
|
|
26
|
+
"bin/liber-tui.py",
|
|
27
|
+
"src/",
|
|
28
|
+
"resources/",
|
|
29
|
+
"liber.yaml",
|
|
30
|
+
"README.md",
|
|
31
|
+
"LICENSE"
|
|
32
|
+
],
|
|
33
|
+
"repository": {
|
|
34
|
+
"type": "git",
|
|
35
|
+
"url": "git+https://github.com/june-arthov/LiBER-.git"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/june-arthov/LiBER-#readme",
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/june-arthov/LiBER-/issues"
|
|
40
|
+
},
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"engines": {
|
|
43
|
+
"node": ">=18"
|
|
44
|
+
}
|
|
45
|
+
}
|
|
Binary file
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="plate" x1="0" y1="0" x2="0" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#1a1918"/><stop offset="1" stop-color="#0e0d0c"/>
|
|
5
|
+
</linearGradient>
|
|
6
|
+
<linearGradient id="gold" x1="0" y1="0" x2="1" y2="1">
|
|
7
|
+
<stop offset="0" stop-color="#F6D084"/><stop offset=".55" stop-color="#E5B25C"/><stop offset="1" stop-color="#B07E2D"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
<radialGradient id="core" cx=".5" cy=".5" r=".5">
|
|
10
|
+
<stop offset="0" stop-color="#FFF3D6"/><stop offset=".55" stop-color="#F6D084"/><stop offset="1" stop-color="#E5B25C"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<rect x="0" y="0" width="256" height="256" rx="46" fill="url(#plate)" stroke="#F6D084" stroke-opacity=".28"/>
|
|
14
|
+
<g fill="none" stroke="url(#gold)" stroke-width="2.5">
|
|
15
|
+
<ellipse cx="128" cy="128" rx="82" ry="30"/>
|
|
16
|
+
<ellipse cx="128" cy="128" rx="82" ry="30" transform="rotate(60 128 128)"/>
|
|
17
|
+
<ellipse cx="128" cy="128" rx="82" ry="30" transform="rotate(120 128 128)"/>
|
|
18
|
+
</g>
|
|
19
|
+
<circle cx="128" cy="128" r="20" fill="url(#core)"/>
|
|
20
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="plate" x1="0" y1="0" x2="0" y2="1">
|
|
4
|
+
<stop offset="0" stop-color="#1a1918"/><stop offset="1" stop-color="#0e0d0c"/>
|
|
5
|
+
</linearGradient>
|
|
6
|
+
<linearGradient id="gold" x1="0" y1="0" x2="1" y2="1">
|
|
7
|
+
<stop offset="0" stop-color="#F6D084"/><stop offset=".55" stop-color="#E5B25C"/><stop offset="1" stop-color="#B07E2D"/>
|
|
8
|
+
</linearGradient>
|
|
9
|
+
<radialGradient id="core" cx=".5" cy=".5" r=".5">
|
|
10
|
+
<stop offset="0" stop-color="#FFF3D6"/><stop offset=".55" stop-color="#F6D084"/><stop offset="1" stop-color="#E5B25C"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<g fill="none" stroke="url(#gold)" stroke-width="2.5">
|
|
14
|
+
<ellipse cx="128" cy="128" rx="82" ry="30"/>
|
|
15
|
+
<ellipse cx="128" cy="128" rx="82" ry="30" transform="rotate(60 128 128)"/>
|
|
16
|
+
<ellipse cx="128" cy="128" rx="82" ry="30" transform="rotate(120 128 128)"/>
|
|
17
|
+
</g>
|
|
18
|
+
<circle cx="128" cy="128" r="20" fill="url(#core)"/>
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Generates the gold atom mark for LiBER: resources/icons/appIcon.png (256),
|
|
2
|
+
// a 512 variant, and a matching SVG. No dependencies — PNG is encoded by hand.
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const zlib = require('zlib');
|
|
6
|
+
|
|
7
|
+
const TRANSPARENT = process.argv.includes('--transparent');
|
|
8
|
+
const SIZE = Number(process.argv[3]) || 256;
|
|
9
|
+
const OUT = process.argv[2] || 'resources/icons/appIcon.png';
|
|
10
|
+
|
|
11
|
+
const GOLD_HI = [246, 208, 132];
|
|
12
|
+
const GOLD = [229, 178, 92];
|
|
13
|
+
const GOLD_LO = [176, 126, 45];
|
|
14
|
+
|
|
15
|
+
function render(W) {
|
|
16
|
+
const px = Buffer.alloc(W * W * 4, 0);
|
|
17
|
+
const s = W / 256; // all geometry is authored at 256 and scaled
|
|
18
|
+
|
|
19
|
+
const blend = (x, y, r, g, b, a) => {
|
|
20
|
+
x = Math.round(x); y = Math.round(y);
|
|
21
|
+
if (x < 0 || y < 0 || x >= W || y >= W || a <= 0) return;
|
|
22
|
+
const i = (y * W + x) * 4;
|
|
23
|
+
const src = Math.min(255, a) / 255;
|
|
24
|
+
const dst = px[i + 3] / 255;
|
|
25
|
+
const out = src + dst * (1 - src);
|
|
26
|
+
if (out <= 0) return;
|
|
27
|
+
px[i] = Math.round((r * src + px[i] * dst * (1 - src)) / out);
|
|
28
|
+
px[i + 1] = Math.round((g * src + px[i + 1] * dst * (1 - src)) / out);
|
|
29
|
+
px[i + 2] = Math.round((b * src + px[i + 2] * dst * (1 - src)) / out);
|
|
30
|
+
px[i + 3] = Math.round(out * 255);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const R = 46 * s;
|
|
34
|
+
const cx0 = W / 2, cy0 = W / 2;
|
|
35
|
+
|
|
36
|
+
// ── plate: dark rounded square with a warm vertical gradient (skipped with --transparent)
|
|
37
|
+
if (!TRANSPARENT) for (let y = 0; y < W; y++) {
|
|
38
|
+
for (let x = 0; x < W; x++) {
|
|
39
|
+
const cx = Math.min(x, W - 1 - x), cy = Math.min(y, W - 1 - y);
|
|
40
|
+
let inside = true;
|
|
41
|
+
if (cx < R && cy < R) inside = (R - cx) ** 2 + (R - cy) ** 2 <= R * R;
|
|
42
|
+
if (!inside) continue;
|
|
43
|
+
const t = y / W;
|
|
44
|
+
blend(x, y, Math.round(26 - 12 * t), Math.round(25 - 12 * t), Math.round(24 - 12 * t), 255);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// ── warm rim
|
|
49
|
+
if (!TRANSPARENT) for (let y = 0; y < W; y++) {
|
|
50
|
+
for (let x = 0; x < W; x++) {
|
|
51
|
+
const cx = Math.min(x, W - 1 - x), cy = Math.min(y, W - 1 - y);
|
|
52
|
+
const edge = (cx === 0 || cy === 0) || (cx < R && cy < R && Math.abs(Math.hypot(R - cx, R - cy) - R) < 1.3 * s);
|
|
53
|
+
if (edge) blend(x, y, GOLD_HI[0], GOLD_HI[1], GOLD_HI[2], 70);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ── nucleus glow (radial falloff, warm)
|
|
58
|
+
const coreR = 20 * s;
|
|
59
|
+
for (let y = 0; y < W; y++) {
|
|
60
|
+
for (let x = 0; x < W; x++) {
|
|
61
|
+
const d = Math.hypot(x - cx0, y - cy0);
|
|
62
|
+
if (d > coreR * 2.6) continue;
|
|
63
|
+
if (d <= coreR) {
|
|
64
|
+
const t = d / coreR;
|
|
65
|
+
blend(x, y,
|
|
66
|
+
Math.round(GOLD_HI[0] + (255 - GOLD_HI[0]) * (1 - t)),
|
|
67
|
+
Math.round(GOLD_HI[1] + (250 - GOLD_HI[1]) * (1 - t)),
|
|
68
|
+
Math.round(GOLD_HI[2] + (225 - GOLD_HI[2]) * (1 - t)), 255);
|
|
69
|
+
} else {
|
|
70
|
+
const t = (d - coreR) / (coreR * 1.6);
|
|
71
|
+
blend(x, y, GOLD[0], GOLD[1], GOLD[2], Math.round(46 * (1 - t)));
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// ── three orbits: ellipse rings at 0°, 60°, 120°
|
|
77
|
+
const A = 82 * s, B = 30 * s, ring = 2.5 * s;
|
|
78
|
+
for (let k = 0; k < 3; k++) {
|
|
79
|
+
const th = (k * Math.PI) / 3;
|
|
80
|
+
const cos = Math.cos(-th), sin = Math.sin(-th);
|
|
81
|
+
for (let y = 0; y < W; y++) {
|
|
82
|
+
for (let x = 0; x < W; x++) {
|
|
83
|
+
const dx = x - cx0, dy = y - cy0;
|
|
84
|
+
if (dx * dx + dy * dy > (A + ring * 3) ** 2) continue;
|
|
85
|
+
const rx = dx * cos - dy * sin;
|
|
86
|
+
const ry = dx * sin + dy * cos;
|
|
87
|
+
const f = (rx / A) ** 2 + (ry / B) ** 2;
|
|
88
|
+
const dist = Math.abs(f - 1) * Math.min(A, B) * 0.55; // approximate px distance
|
|
89
|
+
if (dist > ring * 2.2) continue;
|
|
90
|
+
const a = Math.max(0, 1 - dist / (ring * 2.2));
|
|
91
|
+
const hi = Math.max(0, Math.min(1, (rx + A) / (2 * A))); // brightness along the orbit
|
|
92
|
+
const r = Math.round(GOLD_LO[0] + (GOLD_HI[0] - GOLD_LO[0]) * hi);
|
|
93
|
+
const g = Math.round(GOLD_LO[1] + (GOLD_HI[1] - GOLD_LO[1]) * hi);
|
|
94
|
+
const b = Math.round(GOLD_LO[2] + (GOLD_HI[2] - GOLD_LO[2]) * hi);
|
|
95
|
+
blend(x, y, r, g, b, Math.round(255 * a));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function encodePng(px, W) {
|
|
104
|
+
const raw = Buffer.alloc((W * 4 + 1) * W);
|
|
105
|
+
for (let y = 0; y < W; y++) {
|
|
106
|
+
raw[y * (W * 4 + 1)] = 0;
|
|
107
|
+
px.copy(raw, y * (W * 4 + 1) + 1, y * W * 4, (y + 1) * W * 4);
|
|
108
|
+
}
|
|
109
|
+
const idat = zlib.deflateSync(raw, { level: 9 });
|
|
110
|
+
const table = (() => {
|
|
111
|
+
const t = new Int32Array(256);
|
|
112
|
+
for (let n = 0; n < 256; n++) { let c = n; for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1; t[n] = c; }
|
|
113
|
+
return t;
|
|
114
|
+
})();
|
|
115
|
+
const crc32 = (buf) => { let c = 0xffffffff; for (const b of buf) c = table[(c ^ b) & 0xff] ^ (c >>> 8); return (c ^ 0xffffffff) >>> 0; };
|
|
116
|
+
const chunk = (type, data) => {
|
|
117
|
+
const len = Buffer.alloc(4); len.writeUInt32BE(data.length);
|
|
118
|
+
const t = Buffer.from(type, 'ascii');
|
|
119
|
+
const crc = Buffer.alloc(4); crc.writeUInt32BE(crc32(Buffer.concat([t, data])));
|
|
120
|
+
return Buffer.concat([len, t, data, crc]);
|
|
121
|
+
};
|
|
122
|
+
const ihdr = Buffer.alloc(13);
|
|
123
|
+
ihdr.writeUInt32BE(W, 0); ihdr.writeUInt32BE(W, 4); ihdr[8] = 8; ihdr[9] = 6;
|
|
124
|
+
return Buffer.concat([
|
|
125
|
+
Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]),
|
|
126
|
+
chunk('IHDR', ihdr), chunk('IDAT', idat), chunk('IEND', Buffer.alloc(0))
|
|
127
|
+
]);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const svg = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256" width="256" height="256">
|
|
131
|
+
<defs>
|
|
132
|
+
<linearGradient id="plate" x1="0" y1="0" x2="0" y2="1">
|
|
133
|
+
<stop offset="0" stop-color="#1a1918"/><stop offset="1" stop-color="#0e0d0c"/>
|
|
134
|
+
</linearGradient>
|
|
135
|
+
<linearGradient id="gold" x1="0" y1="0" x2="1" y2="1">
|
|
136
|
+
<stop offset="0" stop-color="#F6D084"/><stop offset=".55" stop-color="#E5B25C"/><stop offset="1" stop-color="#B07E2D"/>
|
|
137
|
+
</linearGradient>
|
|
138
|
+
<radialGradient id="core" cx=".5" cy=".5" r=".5">
|
|
139
|
+
<stop offset="0" stop-color="#FFF3D6"/><stop offset=".55" stop-color="#F6D084"/><stop offset="1" stop-color="#E5B25C"/>
|
|
140
|
+
</radialGradient>
|
|
141
|
+
</defs>
|
|
142
|
+
<rect x="0" y="0" width="256" height="256" rx="46" fill="url(#plate)" stroke="#F6D084" stroke-opacity=".28"/>
|
|
143
|
+
<g fill="none" stroke="url(#gold)" stroke-width="2.5">
|
|
144
|
+
<ellipse cx="128" cy="128" rx="82" ry="30"/>
|
|
145
|
+
<ellipse cx="128" cy="128" rx="82" ry="30" transform="rotate(60 128 128)"/>
|
|
146
|
+
<ellipse cx="128" cy="128" rx="82" ry="30" transform="rotate(120 128 128)"/>
|
|
147
|
+
</g>
|
|
148
|
+
<circle cx="128" cy="128" r="20" fill="url(#core)"/>
|
|
149
|
+
</svg>
|
|
150
|
+
`;
|
|
151
|
+
|
|
152
|
+
const atomTransparentSvg = svg
|
|
153
|
+
.split('\n')
|
|
154
|
+
.filter(line => !line.includes('<rect x="0" y="0"'))
|
|
155
|
+
.join('\n');
|
|
156
|
+
|
|
157
|
+
const dir = path.dirname(OUT);
|
|
158
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
159
|
+
fs.writeFileSync(OUT, encodePng(render(SIZE), SIZE));
|
|
160
|
+
// The plated app icon and the transparent mark are different assets: a
|
|
161
|
+
// transparent run must never overwrite the icon the shell actually ships.
|
|
162
|
+
if (TRANSPARENT) {
|
|
163
|
+
fs.writeFileSync(path.join(dir, 'atom-transparent.svg'), atomTransparentSvg);
|
|
164
|
+
} else {
|
|
165
|
+
fs.writeFileSync(path.join(dir, 'appIcon.svg'), svg);
|
|
166
|
+
fs.writeFileSync(path.join(dir, 'appIcon@512.png'), encodePng(render(512), 512));
|
|
167
|
+
}
|
|
168
|
+
console.log(JSON.stringify({ png: path.resolve(OUT), size: SIZE, transparent: TRANSPARENT, bytes: fs.statSync(OUT).size }));
|