← back to AbramsEgo
AbramsEgo v0.1: self-funding agent command center — live collectors (system/fleet/canaries/crons/sessions/wins/officers), cost+energy P&L with 4 gated revenue engines, dark dashboard UI, Basic Auth, stale-while-revalidate snapshot
04aa741ee7b05b15d12b84ec277b6d3ffe5e6cbc · 2026-07-01 10:14:19 -0700 · Steve (AbramsEgo)
Files touched
A data/revenue.jsonA package-lock.jsonM package.jsonA public/index.htmlA server.js
Diff
commit 04aa741ee7b05b15d12b84ec277b6d3ffe5e6cbc
Author: Steve (AbramsEgo) <steve@designerwallcoverings.com>
Date: Wed Jul 1 10:14:19 2026 -0700
AbramsEgo v0.1: self-funding agent command center — live collectors (system/fleet/canaries/crons/sessions/wins/officers), cost+energy P&L with 4 gated revenue engines, dark dashboard UI, Basic Auth, stale-while-revalidate snapshot
---
data/revenue.json | 33 +++
package-lock.json | 867 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
package.json | 2 +-
public/index.html | 240 +++++++++++++++
server.js | 364 +++++++++++++++++++++++
5 files changed, 1505 insertions(+), 1 deletion(-)
diff --git a/data/revenue.json b/data/revenue.json
new file mode 100644
index 0000000..6300640
--- /dev/null
+++ b/data/revenue.json
@@ -0,0 +1,33 @@
+{
+ "updatedAt": "2026-07-01T17:13:33.202Z",
+ "engines": [
+ {
+ "key": "sell_product",
+ "label": "Sell AbramsEgo (template/SaaS)",
+ "status": "gated",
+ "amount": 0,
+ "note": "Stripe + pricing + public landing — awaiting Steve go-live"
+ },
+ {
+ "key": "affiliate",
+ "label": "Affiliate / commission",
+ "status": "gated",
+ "amount": 0,
+ "note": "DW referral + directory lead-gen — awaiting affiliate signup + placement"
+ },
+ {
+ "key": "billable_work",
+ "label": "Agent does billable work",
+ "status": "gated",
+ "amount": 0,
+ "note": "arbitrage / paid reports / metered API — awaiting billing rail"
+ },
+ {
+ "key": "ads",
+ "label": "Ads on a public property",
+ "status": "gated",
+ "amount": 0,
+ "note": "ad network + public site — awaiting Steve go-live"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..45d9ff3
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,867 @@
+{
+ "name": "abramsego",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "abramsego",
+ "version": "0.1.0",
+ "dependencies": {
+ "basic-auth": "^2.0.1",
+ "express": "^4.22.2"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "license": "MIT"
+ },
+ "node_modules/basic-auth": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
+ "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.5",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz",
+ "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.15.1",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-disposition/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "license": "MIT"
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.2",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz",
+ "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.5",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.15.1",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz",
+ "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "license": "ISC"
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz",
+ "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==",
+ "license": "MIT"
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.15.3",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
+ "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "license": "MIT"
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "license": "ISC"
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.4"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
index 798d1d5..d83a963 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,6 @@
},
"dependencies": {
"basic-auth": "^2.0.1",
- "express": "^4.21.2"
+ "express": "^4.22.2"
}
}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..a750855
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,240 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8" />
+<meta name="viewport" content="width=device-width, initial-scale=1" />
+<title>AbramsEgo · Command Center</title>
+<style>
+ :root{
+ --bg:#0a0e14; --panel:#111823; --panel2:#0d141d; --line:#1e2a3a; --ink:#dbe6f3;
+ --dim:#7d8fa6; --accent:#4d9fff; --good:#3fd18b; --warn:#ffb454; --bad:#ff5d6c;
+ --gold:#ffd166; --mono:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;
+ }
+ *{box-sizing:border-box}
+ body{margin:0;background:radial-gradient(1200px 600px at 70% -10%,#12203300,#0a0e14),var(--bg);
+ color:var(--ink);font:14px/1.45 var(--mono);-webkit-font-smoothing:antialiased}
+ header{display:flex;align-items:center;gap:14px;padding:12px 18px;border-bottom:1px solid var(--line);
+ position:sticky;top:0;background:rgba(10,14,20,.9);backdrop-filter:blur(8px);z-index:5}
+ .logo{font-weight:700;letter-spacing:.5px;font-size:16px}
+ .logo b{color:var(--accent)}
+ .metrics{display:flex;gap:16px;margin-left:auto;flex-wrap:wrap;align-items:center}
+ .metric{display:flex;flex-direction:column;align-items:flex-end;line-height:1.1}
+ .metric .v{font-size:15px;font-weight:600}
+ .metric .k{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.6px}
+ .dot{width:8px;height:8px;border-radius:50%;display:inline-block;margin-right:5px}
+ .refresh{font-size:11px;color:var(--dim)}
+ main{padding:16px;display:grid;grid-template-columns:repeat(12,1fr);gap:14px;max-width:1500px;margin:0 auto}
+ .card{background:linear-gradient(180deg,var(--panel),var(--panel2));border:1px solid var(--line);
+ border-radius:12px;padding:14px 16px;min-height:60px}
+ .card h2{margin:0 0 10px;font-size:11px;letter-spacing:1px;text-transform:uppercase;color:var(--dim);
+ display:flex;align-items:center;gap:8px}
+ .span12{grid-column:span 12}.span8{grid-column:span 8}.span6{grid-column:span 6}
+ .span4{grid-column:span 4}.span3{grid-column:span 3}
+ @media(max-width:1000px){.span8,.span6,.span4,.span3{grid-column:span 12}}
+ .big{font-size:30px;font-weight:700}
+ .good{color:var(--good)}.warn{color:var(--warn)}.bad{color:var(--bad)}.gold{color:var(--gold)}.dim{color:var(--dim)}
+ .row{display:flex;justify-content:space-between;gap:10px;padding:4px 0;border-bottom:1px dashed #ffffff08}
+ .row:last-child{border-bottom:0}
+ .pill{font-size:10px;padding:2px 7px;border-radius:20px;border:1px solid var(--line);color:var(--dim);white-space:nowrap}
+ .pill.gated{color:var(--warn);border-color:#ffb45444;background:#ffb4540f}
+ .pill.live{color:var(--good);border-color:#3fd18b44;background:#3fd18b0f}
+ .bar{height:6px;border-radius:6px;background:#ffffff10;overflow:hidden;margin-top:6px}
+ .bar>span{display:block;height:100%;background:var(--accent)}
+ .pnlgrid{display:grid;grid-template-columns:repeat(4,1fr);gap:14px}
+ @media(max-width:800px){.pnlgrid{grid-template-columns:repeat(2,1fr)}}
+ .stat{background:#0c1420;border:1px solid var(--line);border-radius:10px;padding:12px}
+ .stat .k{font-size:10px;color:var(--dim);text-transform:uppercase;letter-spacing:.6px}
+ .stat .v{font-size:22px;font-weight:700;margin-top:3px}
+ .eng{display:flex;align-items:center;justify-content:space-between;gap:10px;padding:7px 0;border-bottom:1px dashed #ffffff08}
+ .list{max-height:240px;overflow:auto}
+ .list::-webkit-scrollbar{width:6px}.list::-webkit-scrollbar-thumb{background:#ffffff18;border-radius:6px}
+ .mono{font-family:var(--mono)}
+ .chat input{width:100%;background:#0c1420;border:1px solid var(--line);color:var(--ink);
+ border-radius:8px;padding:10px;font-family:var(--mono)}
+ .chat .ans{margin-top:10px;color:var(--ink);white-space:pre-wrap;background:#0c1420;border:1px solid var(--line);
+ border-radius:8px;padding:10px;min-height:20px}
+ .muted{color:var(--dim);font-size:11px}
+ a{color:var(--accent);text-decoration:none}
+ .alert{grid-column:span 12;background:#ff5d6c14;border:1px solid #ff5d6c44;color:#ffd7db;
+ border-radius:10px;padding:8px 14px;font-size:12px;display:none}
+ .alert.show{display:block}
+</style>
+</head>
+<body>
+<header>
+ <div class="logo"><b>Abrams</b>Ego <span class="dim" style="font-size:11px">· command center</span></div>
+ <div class="metrics" id="metrics"></div>
+ <div class="refresh" id="refresh">…</div>
+</header>
+<main id="main">
+ <div class="alert" id="alert"></div>
+
+ <!-- SELF-FUNDING P&L (the money panel) -->
+ <section class="card span8">
+ <h2>💰 Self-Funding P&L <span class="muted" id="pnlwin">· today</span></h2>
+ <div class="pnlgrid">
+ <div class="stat"><div class="k">Cost today</div><div class="v" id="pnl-cost">—</div><div class="muted" id="pnl-cost-sub"></div></div>
+ <div class="stat"><div class="k">Revenue today</div><div class="v gold" id="pnl-rev">—</div><div class="muted">4 engines · gated</div></div>
+ <div class="stat"><div class="k">Net</div><div class="v" id="pnl-net">—</div><div class="muted">revenue − cost</div></div>
+ <div class="stat"><div class="k">Self-funding</div><div class="v" id="pnl-pct">—</div><div class="muted">of run cost covered</div></div>
+ </div>
+ <div style="margin-top:12px" id="engines"></div>
+ </section>
+
+ <!-- COST BREAKDOWN -->
+ <section class="card span4">
+ <h2>🔌 Cost to run · 30d</h2>
+ <div class="big" id="cost30">—</div>
+ <div class="muted" id="cost30sub"></div>
+ <div style="margin-top:10px" id="costapps"></div>
+ </section>
+
+ <!-- FLEET -->
+ <section class="card span4">
+ <h2>🛰 Fleet · Kamatera</h2>
+ <div class="big" id="kam-up">—</div>
+ <div class="list" id="kam-list" style="margin-top:8px"></div>
+ </section>
+ <section class="card span4">
+ <h2>🖥 Fleet · Local</h2>
+ <div class="big" id="loc-up">—</div>
+ <div class="list" id="loc-list" style="margin-top:8px"></div>
+ </section>
+
+ <!-- CANARIES -->
+ <section class="card span4">
+ <h2>🐤 Canary board</h2>
+ <div class="list" id="canaries"></div>
+ </section>
+
+ <!-- SCHEDULE + OFFICERS -->
+ <section class="card span3">
+ <h2>⏱ launchd jobs</h2>
+ <div class="big" id="cron-loaded">—</div>
+ <div class="muted">loaded / total com.steve.*</div>
+ </section>
+ <section class="card span3">
+ <h2>🏛 Officers</h2>
+ <div class="big" id="off-count">—</div>
+ <div class="list" id="officers" style="margin-top:8px"></div>
+ </section>
+
+ <!-- WINS -->
+ <section class="card span6">
+ <h2>🏆 Wins <span class="muted" id="wins-today"></span></h2>
+ <div class="list" id="wins"></div>
+ </section>
+
+ <!-- SESSIONS -->
+ <section class="card span6">
+ <h2>💬 Live Claude sessions</h2>
+ <div class="list" id="sessions"></div>
+ </section>
+
+ <!-- AI CHAT -->
+ <section class="card span12 chat">
+ <h2>🤖 Ask AbramsEgo <span class="muted">· grounded in the live snapshot · cost shown per query</span></h2>
+ <input id="q" placeholder="e.g. what did the fleet do overnight? is it paying for itself?" />
+ <div class="ans" id="ans">Ask a question about your fleet, cost, or P&L.</div>
+ </section>
+</main>
+
+<script>
+const $ = (id)=>document.getElementById(id);
+const usd = (n)=> n==null? '—' : '$'+Number(n).toFixed(n<1?4:2);
+const cls = (pct,warn,bad)=> pct>=bad?'bad':pct>=warn?'warn':'good';
+
+async function load(){
+ let d; try{ d = await (await fetch('/api/data')).json(); }catch(e){ return; }
+ if(!d || !d.builtAt) return;
+ // metrics bar
+ const s=d.system||{};
+ $('metrics').innerHTML = [
+ ['CPU', (s.loadPct??'—')+'%', cls(s.loadPct,60,85)],
+ ['RAM', (s.memUsedPct??'—')+'%', cls(s.memUsedPct,70,88)],
+ ['DISK', (s.diskUsedPct??'—')+'%', cls(s.diskUsedPct,80,90)],
+ ['NODE', s.node||'—',''],
+ ['UP', fmtDur(s.dashboardUptimeSec)||'—',''],
+ ].map(([k,v,c])=>`<div class="metric"><div class="v ${c}">${v}</div><div class="k">${k}</div></div>`).join('');
+ $('refresh').textContent = 'snapshot '+new Date(d.builtAt).toLocaleTimeString()+' · auto 15s';
+
+ // ---- P&L ----
+ const p=(d.pnl&&d.pnl.today)||{}, c=(d.cost&&d.cost.today)||{};
+ $('pnl-cost').textContent=usd(p.cost);
+ $('pnl-cost-sub').textContent=`tokens ${usd(c.tokens)} + energy ${usd(c.energy)} est`;
+ $('pnl-rev').textContent=usd(p.revenue);
+ $('pnl-net').textContent=usd(p.net);
+ $('pnl-net').className='v '+((p.net||0)>=0?'good':'bad');
+ $('pnl-pct').textContent=(p.selfFundingPct==null?'0':p.selfFundingPct)+'%';
+ $('pnl-pct').className='v '+((p.selfFundingPct||0)>=100?'good':(p.selfFundingPct||0)>0?'warn':'bad');
+ const engs=(d.revenue&&d.revenue.engines)||[];
+ $('engines').innerHTML = engs.map(e=>`<div class="eng"><span>${e.label} <span class="muted">${e.note}</span></span>
+ <span class="pill ${e.status==='live'?'live':'gated'}">${e.status==='live'?usd(e.amount):'GATED'}</span></div>`).join('');
+
+ // ---- cost 30d ----
+ const c30=(d.cost&&d.cost.month)||{};
+ $('cost30').textContent=usd(c30.total);
+ $('cost30sub').textContent=`tokens ${usd(c30.tokens)} (${c30.tokensCount||0} calls) + energy ${usd(c30.energy)} est`;
+ const apps=Object.entries(c30.byApp||{}).sort((a,b)=>b[1]-a[1]).slice(0,6);
+ const max=Math.max(...apps.map(a=>a[1]),0.0001);
+ $('costapps').innerHTML=apps.map(([a,v])=>`<div class="row"><span class="dim">${a}</span><span>${usd(v)}</span></div><div class="bar"><span style="width:${Math.round(v/max*100)}%"></span></div>`).join('')||'<span class="muted">no cost data</span>';
+
+ // ---- fleet ----
+ const k=d.kamatera||{}, l=d.localFleet||{};
+ $('kam-up').innerHTML=`<span class="${cls(100-(k.up/(k.total||1)*100),20,50)}">${k.up??'—'}</span><span class="dim" style="font-size:16px">/${k.total??'—'} up</span>`;
+ $('kam-list').innerHTML=(k.procs||[]).slice(0,40).map(pr=>`<div class="row"><span>${statusDot(pr.status)}${pr.name}</span><span class="dim">${pr.restarts??''}↺</span></div>`).join('')|| (k.note?`<span class="muted">${k.note}</span>`:'');
+ $('loc-up').innerHTML=`<span class="good">${l.up??'—'}</span><span class="dim" style="font-size:16px">/${l.total??'—'} up</span>`;
+ $('loc-list').innerHTML=(l.procs||[]).map(pr=>`<div class="row"><span>${statusDot(pr.status)}${pr.name}</span><span class="dim">${pr.memMB!=null?pr.memMB+'M':''}</span></div>`).join('')||'<span class="muted">no local pm2 procs</span>';
+
+ // ---- canaries ----
+ const cans=(d.canaries&&d.canaries.canaries)||[];
+ $('canaries').innerHTML=cans.map(cn=>{
+ const v=(cn.verdict||'').toUpperCase();
+ const col=/FAIL|DOWN|CRIT/.test(v)?'bad':/WARN|DEGRAD/.test(v)?'warn':/PASS|UP|HEALTH|OK/.test(v)?'good':'dim';
+ return `<div class="row"><span>${cn.name}</span><span class="${col}">${cn.verdict||'—'} <span class="dim">${cn.ageMin}m${cn.stale?' ⚠':''}</span></span></div>`;
+ }).join('')||'<span class="muted">no canaries</span>';
+
+ // ---- crons / officers ----
+ const cr=d.crons||{};
+ $('cron-loaded').innerHTML=`<span class="${cls(100-(cr.loadedCount/(cr.count||1)*100),40,70)}">${cr.loadedCount??'—'}</span><span class="dim" style="font-size:16px">/${cr.count??'—'}</span>`;
+ const off=d.officers||{};
+ $('off-count').textContent=off.count??'—';
+ $('officers').innerHTML=(off.officers||[]).map(o=>`<div class="row"><span>${o.vp||'—'}</span><span class="dim">${o.domain||''}</span></div>`).join('');
+
+ // ---- wins ----
+ const w=d.wins||{};
+ $('wins-today').textContent=`· ${w.today||0} today · ${w.count||0} total`;
+ $('wins').innerHTML=(w.recent||[]).map(x=>`<div class="row"><span>${esc(x.title)} <span class="dim">${x.project||''}</span></span><span class="dim">${(x.date||'').slice(5,10)}</span></div>`).join('')||'<span class="muted">no wins logged</span>';
+
+ // ---- sessions ----
+ const ss=(d.sessions&&d.sessions.recent)||[];
+ $('sessions').innerHTML=ss.map(x=>`<div class="row"><span>${esc(x.title||x.cwd||'session')}</span><span class="dim">${x.msgCount!=null?x.msgCount+' msg':''}</span></div>`).join('')||'<span class="muted">no sessions</span>';
+
+ // ---- alert banner ----
+ const alerts=[];
+ if((s.diskUsedPct||0)>=90) alerts.push('disk '+s.diskUsedPct+'%');
+ if((s.memUsedPct||0)>=88) alerts.push('RAM '+s.memUsedPct+'%');
+ const failCan=cans.filter(x=>/FAIL|DOWN|CRIT/.test((x.verdict||'').toUpperCase()));
+ if(failCan.length) alerts.push(failCan.length+' canary FAIL');
+ if((p.selfFundingPct||0)<100) alerts.push('not yet self-funding ('+ (p.selfFundingPct||0) +'% of cost covered)');
+ const a=$('alert'); if(alerts.length){a.classList.add('show');a.textContent='⚠ '+alerts.join(' · ');}else{a.classList.remove('show');}
+}
+function statusDot(st){const c=st==='online'?'var(--good)':st?'var(--bad)':'var(--dim)';return `<span class="dot" style="background:${c}"></span>`;}
+function fmtDur(s){if(s==null)return null;if(s<60)return s+'s';if(s<3600)return Math.floor(s/60)+'m';return Math.floor(s/3600)+'h';}
+function esc(x){return (x==null?'':String(x)).replace(/[<>&]/g,m=>({'<':'<','>':'>','&':'&'}[m]));}
+
+// chat
+$('q').addEventListener('keydown',async e=>{
+ if(e.key!=='Enter')return;
+ const q=$('q').value.trim(); if(!q)return;
+ $('ans').textContent='…thinking';
+ try{
+ const r=await (await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({q})})).json();
+ $('ans').textContent=(r.answer||r.error||'no answer')+`\n\n— ${r.engine||'?'} · cost ${usd(r.cost_usd||0)}`;
+ }catch(e){ $('ans').textContent='chat error'; }
+});
+
+load(); setInterval(load, 15000);
+</script>
+</body>
+</html>
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..d5ad62b
--- /dev/null
+++ b/server.js
@@ -0,0 +1,364 @@
+/*
+ * AbramsEgo — self-funding AI-agent command center.
+ *
+ * A single-page dashboard over Steve's real fleet (OpenClaw / @androoagi
+ * "agent environment" style), plus a self-funding P&L: it accounts for its own
+ * token + energy COST and tracks REVENUE from four (gated) engines so it can
+ * pay for itself. "Claude needs to be paid for."
+ *
+ * Design: a 30s background buildSnapshot() gathers every (slow) source once and
+ * writes data/data.json; every /api/* serves that cache instantly
+ * (stale-while-revalidate). One source failing never kills the snapshot.
+ *
+ * Safe/reversible by construction: read-only against the fleet, Basic-Auth
+ * gated, binds locally. No money mechanism is wired here — revenue engines are
+ * declared with status:"gated" until Steve flips them on.
+ */
+'use strict';
+
+try { require('dotenv').config({ path: require('path').join(__dirname, '.env') }); } catch (e) {}
+
+const express = require('express');
+const basicAuth = require('basic-auth');
+const path = require('path');
+const fs = require('fs');
+const os = require('os');
+const { execFile } = require('child_process');
+
+const PORT = parseInt(process.env.PORT, 10) || 9773;
+const HOME = os.homedir();
+const DATA_DIR = path.join(__dirname, 'data');
+const SNAPSHOT = path.join(DATA_DIR, 'data.json');
+const REVENUE_FILE = path.join(DATA_DIR, 'revenue.json');
+const VERSION = require('./package.json').version;
+const START_TS = Date.now();
+
+// --- self-funding knobs (env-tunable, all estimates clearly labeled) ---------
+const ENERGY_AVG_WATTS = parseFloat(process.env.ENERGY_AVG_WATTS || '90'); // Mac Studio ~30W idle → ~140W load
+const ENERGY_RATE = parseFloat(process.env.ENERGY_RATE_USD_PER_KWH || '0.30'); // CA residential ~$0.30/kWh
+const CNCP = process.env.CNCP_BASE || 'http://127.0.0.1:3333';
+const KAMATERA = process.env.KAMATERA_HOST || '45.61.58.125';
+const COST_LEDGER = path.join(HOME, '.claude', 'cost-ledger.jsonl');
+
+fs.mkdirSync(DATA_DIR, { recursive: true });
+
+// ---------------------------------------------------------------------------
+// small helpers
+// ---------------------------------------------------------------------------
+function sh(cmd, args, timeoutMs = 8000) {
+ return new Promise((resolve) => {
+ execFile(cmd, args, { timeout: timeoutMs, maxBuffer: 1024 * 1024 * 16 }, (err, stdout) => {
+ resolve({ ok: !err, out: (stdout || '').toString(), err: err && err.message });
+ });
+ });
+}
+async function fetchJSON(url, timeoutMs = 5000) {
+ const ctrl = new AbortController();
+ const t = setTimeout(() => ctrl.abort(), timeoutMs);
+ try {
+ const r = await fetch(url, { signal: ctrl.signal });
+ if (!r.ok) return null;
+ return await r.json();
+ } catch (e) { return null; } finally { clearTimeout(t); }
+}
+const round = (n, d = 4) => Math.round((n + Number.EPSILON) * 10 ** d) / 10 ** d;
+
+// ---------------------------------------------------------------------------
+// collectors — each returns a plain object; each is wrapped in try/catch by the
+// snapshot builder so a single failure degrades to {error} rather than crashing.
+// ---------------------------------------------------------------------------
+
+async function collectSystem() {
+ const load = os.loadavg(); // [1,5,15] min
+ const cpus = os.cpus().length;
+ const totalMem = os.totalmem(), freeMem = os.freemem();
+ const memUsedPct = round((1 - freeMem / totalMem) * 100, 1);
+ const loadPct = round((load[0] / cpus) * 100, 1);
+ // disk of home volume
+ let diskPct = null, diskInfo = null;
+ const df = await sh('df', ['-k', HOME], 4000);
+ if (df.ok) {
+ const line = df.out.trim().split('\n').pop().split(/\s+/);
+ // Filesystem 1K-blocks Used Avail Capacity ... Mounted
+ const capIdx = line.findIndex((x) => /%$/.test(x));
+ if (capIdx >= 0) diskPct = parseInt(line[capIdx], 10);
+ diskInfo = { usedPct: diskPct };
+ }
+ // swap (macOS)
+ let swap = null;
+ const sm = await sh('sysctl', ['-n', 'vm.swapusage'], 3000);
+ if (sm.ok) {
+ const m = sm.out.match(/used\s*=\s*([\d.]+)([MG])/i);
+ if (m) swap = { usedMB: m[2].toUpperCase() === 'G' ? parseFloat(m[1]) * 1024 : parseFloat(m[1]) };
+ }
+ return {
+ host: os.hostname(),
+ platform: `${os.type()} ${os.release()}`,
+ cpus, load1: round(load[0], 2), loadPct,
+ memUsedPct, memUsedGB: round((totalMem - freeMem) / 1e9, 1), memTotalGB: round(totalMem / 1e9, 1),
+ diskUsedPct: diskPct, swap,
+ node: process.version, dashboardUptimeSec: Math.round((Date.now() - START_TS) / 1000),
+ };
+}
+
+async function collectLocalFleet() {
+ const pm2Bin = path.join(HOME, '.npm-global', 'bin', 'pm2');
+ const r = await sh(pm2Bin, ['jlist'], 8000);
+ if (!r.ok) return { procs: [], error: r.err || 'pm2 unavailable' };
+ let list = [];
+ try { list = JSON.parse(r.out); } catch (e) { return { procs: [], error: 'pm2 parse' }; }
+ const procs = list.map((p) => ({
+ name: p.name,
+ status: p.pm2_env && p.pm2_env.status,
+ cpu: p.monit && p.monit.cpu,
+ memMB: p.monit ? round(p.monit.memory / 1e6, 0) : null,
+ restarts: p.pm2_env && p.pm2_env.restart_time,
+ uptimeMs: p.pm2_env && p.pm2_env.pm_uptime ? Date.now() - p.pm2_env.pm_uptime : null,
+ }));
+ return { procs, up: procs.filter((p) => p.status === 'online').length, total: procs.length };
+}
+
+// Kamatera fleet is fetched on a slower cadence and cached to disk, because SSH
+// round-trips are too slow for the 30s snapshot loop.
+const KAM_CACHE = path.join(DATA_DIR, 'kamatera-fleet.json');
+let kamLastFetch = 0;
+async function collectKamateraFleet() {
+ const now = Date.now();
+ // refresh at most every 5 min
+ if (now - kamLastFetch > 5 * 60 * 1000) {
+ kamLastFetch = now;
+ const r = await sh('ssh', ['-o', 'ConnectTimeout=6', '-o', 'BatchMode=yes', `root@${KAMATERA}`, 'pm2 jlist'], 12000);
+ if (r.ok) {
+ try {
+ const list = JSON.parse(r.out);
+ const procs = list.map((p) => ({ name: p.name, status: p.pm2_env && p.pm2_env.status, restarts: p.pm2_env && p.pm2_env.restart_time }));
+ fs.writeFileSync(KAM_CACHE, JSON.stringify({ fetchedAt: new Date().toISOString(), procs }));
+ } catch (e) { /* keep last-good */ }
+ }
+ }
+ try {
+ const c = JSON.parse(fs.readFileSync(KAM_CACHE, 'utf8'));
+ return { fetchedAt: c.fetchedAt, procs: c.procs, up: c.procs.filter((p) => p.status === 'online').length, total: c.procs.length };
+ } catch (e) { return { procs: [], up: 0, total: 0, note: 'no cached kamatera snapshot yet' }; }
+}
+
+async function collectCanaries() {
+ const glob = path.join(HOME, '.claude', 'skills');
+ let names = [];
+ try { names = fs.readdirSync(glob); } catch (e) { return { canaries: [] }; }
+ const out = [];
+ for (const name of names) {
+ const f = path.join(glob, name, 'data', 'latest.json');
+ try {
+ const st = fs.statSync(f);
+ let verdict = null, scanned = null;
+ try {
+ const j = JSON.parse(fs.readFileSync(f, 'utf8'));
+ verdict = j.verdict || j.status || (j.down > 0 ? 'DOWN' : j.degraded > 0 ? 'DEGRADED' : (j.up != null ? 'UP' : null));
+ scanned = j.scanned_at || j.checked_at || null;
+ } catch (e) {}
+ const ageMin = Math.round((Date.now() - st.mtimeMs) / 60000);
+ out.push({ name, verdict, scanned, ageMin, stale: ageMin > 24 * 60 });
+ } catch (e) { /* no heartbeat */ }
+ }
+ out.sort((a, b) => a.ageMin - b.ageMin);
+ return { canaries: out, count: out.length };
+}
+
+async function collectCrons() {
+ const laDir = path.join(HOME, 'Library', 'LaunchAgents');
+ let files = [];
+ try { files = fs.readdirSync(laDir).filter((f) => /^com\.steve\..*\.plist$/.test(f)); } catch (e) {}
+ // loaded state
+ const ll = await sh('launchctl', ['list'], 4000);
+ const loaded = {};
+ if (ll.ok) {
+ ll.out.trim().split('\n').forEach((line) => {
+ const cols = line.split(/\s+/);
+ const label = cols[cols.length - 1];
+ if (label && label.startsWith('com.steve.')) loaded[label] = { pid: cols[0], exit: cols[1] };
+ });
+ }
+ const jobs = files.map((f) => {
+ const label = f.replace(/\.plist$/, '');
+ const l = loaded[label];
+ return { label, loaded: !!l, pid: l && l.pid !== '-' ? l.pid : null, lastExit: l ? l.exit : null };
+ });
+ return { count: jobs.length, loadedCount: jobs.filter((j) => j.loaded).length, jobs };
+}
+
+// --- COST side of the P&L --------------------------------------------------
+function readCostLedger(sinceMs) {
+ // cost-ledger.jsonl: {ts, api, vendor, app, cost_usd, units:[...]}
+ let total = 0, count = 0; const byApp = {}, byVendor = {};
+ try {
+ const lines = fs.readFileSync(COST_LEDGER, 'utf8').split('\n');
+ for (const ln of lines) {
+ if (!ln.trim()) continue;
+ let j; try { j = JSON.parse(ln); } catch (e) { continue; }
+ const t = Date.parse(j.ts);
+ if (isNaN(t) || t < sinceMs) continue;
+ const c = j.cost_usd || 0;
+ total += c; count++;
+ const app = j.app || 'unknown'; const v = j.vendor || 'unknown';
+ byApp[app] = (byApp[app] || 0) + c;
+ byVendor[v] = (byVendor[v] || 0) + c;
+ }
+ } catch (e) {}
+ return { total: round(total, 4), count, byApp, byVendor };
+}
+function energyCost(sinceMs) {
+ const hours = (Date.now() - sinceMs) / 3.6e6;
+ const kwh = (ENERGY_AVG_WATTS / 1000) * hours;
+ return { hours: round(hours, 1), kwh: round(kwh, 3), usd: round(kwh * ENERGY_RATE, 4), est: true, avgWatts: ENERGY_AVG_WATTS, rate: ENERGY_RATE };
+}
+function collectCost() {
+ const now = Date.now();
+ const windows = { today: 24 * 3.6e6, week: 7 * 24 * 3.6e6, month: 30 * 24 * 3.6e6 };
+ const out = {};
+ for (const [k, ms] of Object.entries(windows)) {
+ const since = now - ms;
+ const tokens = readCostLedger(since);
+ const energy = energyCost(since);
+ out[k] = { tokens: tokens.total, tokensCount: tokens.count, energy: energy.usd, energyKwh: energy.kwh, total: round(tokens.total + energy.usd, 4), byApp: tokens.byApp };
+ }
+ return out;
+}
+
+// --- REVENUE side of the P&L (four gated engines) --------------------------
+function defaultRevenue() {
+ return {
+ updatedAt: new Date().toISOString(),
+ engines: [
+ { key: 'sell_product', label: 'Sell AbramsEgo (template/SaaS)', status: 'gated', amount: 0, note: 'Stripe + pricing + public landing — awaiting Steve go-live' },
+ { key: 'affiliate', label: 'Affiliate / commission', status: 'gated', amount: 0, note: 'DW referral + directory lead-gen — awaiting affiliate signup + placement' },
+ { key: 'billable_work', label: 'Agent does billable work', status: 'gated', amount: 0, note: 'arbitrage / paid reports / metered API — awaiting billing rail' },
+ { key: 'ads', label: 'Ads on a public property', status: 'gated', amount: 0, note: 'ad network + public site — awaiting Steve go-live' },
+ ],
+ };
+}
+function collectRevenue() {
+ try { return JSON.parse(fs.readFileSync(REVENUE_FILE, 'utf8')); }
+ catch (e) { const d = defaultRevenue(); try { fs.writeFileSync(REVENUE_FILE, JSON.stringify(d, null, 2)); } catch (e2) {} return d; }
+}
+
+function buildPnL(cost, revenue) {
+ const out = {};
+ for (const win of ['today', 'week', 'month']) {
+ const c = (cost[win] && cost[win].total) || 0;
+ // revenue is a running total; for windows we currently only have live totals → attribute all to "month", 0 else until engines log dated income.
+ const rev = win === 'month' ? (revenue.engines || []).reduce((s, e) => s + (e.amount || 0), 0) : (revenue.engines || []).reduce((s, e) => s + (e.amount || 0), 0);
+ const net = round(rev - c, 4);
+ out[win] = { cost: round(c, 4), revenue: round(rev, 4), net, selfFundingPct: c > 0 ? round((rev / c) * 100, 1) : null };
+ }
+ return out;
+}
+
+// ---------------------------------------------------------------------------
+// snapshot builder + stale-while-revalidate cache
+// ---------------------------------------------------------------------------
+let SNAP = { builtAt: null, building: false };
+async function buildSnapshot() {
+ if (SNAP.building) return SNAP;
+ SNAP.building = true;
+ const wrap = async (fn) => { try { return await fn(); } catch (e) { return { error: e.message }; } };
+ try {
+ const [system, localFleet, kamatera, canaries, crons] = await Promise.all([
+ wrap(collectSystem), wrap(collectLocalFleet), wrap(collectKamateraFleet), wrap(collectCanaries), wrap(collectCrons),
+ ]);
+ const [sessions, wins, officers] = await Promise.all([
+ wrap(() => fetchJSON(`${CNCP}/api/sessions`)),
+ wrap(() => fetchJSON(`${CNCP}/api/wins`)),
+ wrap(() => fetchJSON(`${CNCP}/api/officers`)),
+ ]);
+ const cost = wrap(collectCost) instanceof Promise ? await wrap(collectCost) : collectCost();
+ const revenue = collectRevenue();
+ const pnl = buildPnL(cost, revenue);
+ SNAP = {
+ builtAt: new Date().toISOString(),
+ building: false,
+ version: VERSION,
+ system, localFleet, kamatera, canaries, crons,
+ sessions: summarizeSessions(sessions),
+ wins: summarizeWins(wins),
+ officers: summarizeOfficers(officers),
+ cost, revenue, pnl,
+ };
+ try { fs.writeFileSync(SNAPSHOT, JSON.stringify(SNAP)); } catch (e) {}
+ } finally { SNAP.building = false; }
+ return SNAP;
+}
+function summarizeSessions(s) {
+ const arr = Array.isArray(s) ? s : (s && s.sessions) || [];
+ return { count: arr.length, recent: arr.slice(0, 12) };
+}
+function summarizeWins(w) {
+ const arr = Array.isArray(w) ? w : (w && w.wins) || [];
+ const today = new Date().toISOString().slice(0, 10);
+ return { count: arr.length, today: arr.filter((x) => (x.date || '').slice(0, 10) === today).length, recent: arr.slice(0, 10) };
+}
+function summarizeOfficers(o) {
+ const arr = Array.isArray(o) ? o : (o && (o.cabinet || o.officers)) || [];
+ return {
+ count: arr.length,
+ officers: arr.map((x) => ({ vp: x.vp || x.name || x.id, domain: x.domain || (Array.isArray(x.triggers) ? x.triggers[0] : '') })),
+ };
+}
+
+// boot: load last snapshot from disk if present, then refresh
+try { SNAP = Object.assign(JSON.parse(fs.readFileSync(SNAPSHOT, 'utf8')), { building: false }); } catch (e) {}
+buildSnapshot();
+setInterval(() => { buildSnapshot(); }, 30 * 1000);
+
+// ---------------------------------------------------------------------------
+// http
+// ---------------------------------------------------------------------------
+const app = express();
+app.disable('x-powered-by');
+app.use(express.json({ limit: '256kb' }));
+
+// Basic-Auth gate on everything EXCEPT /api/healthz (smoke tests + uptime probes)
+const AUTH_USER = process.env.ABRAMSEGO_USER || 'admin';
+const AUTH_PASS = process.env.ABRAMSEGO_PASS || 'DW2024!';
+app.use((req, res, next) => {
+ if (req.path === '/api/healthz') return next();
+ const cred = basicAuth(req);
+ if (cred && cred.name === AUTH_USER && cred.pass === AUTH_PASS) return next();
+ res.set('WWW-Authenticate', 'Basic realm="AbramsEgo"');
+ return res.status(401).send('Auth required');
+});
+
+app.get('/api/healthz', (req, res) => res.json({ ok: true, service: 'abramsego', version: VERSION, uptimeSec: Math.round((Date.now() - START_TS) / 1000), snapshotAt: SNAP.builtAt }));
+app.get('/api/data', (req, res) => { res.json(SNAP); });
+app.get('/api/refresh', async (req, res) => { const s = await buildSnapshot(); res.json(s); });
+// per-panel slices (served from cache)
+for (const key of ['system', 'localFleet', 'kamatera', 'canaries', 'crons', 'sessions', 'wins', 'officers', 'cost', 'revenue', 'pnl']) {
+ app.get(`/api/${key}`, (req, res) => res.json(SNAP[key] || {}));
+}
+
+// AI chat — grounded in the current snapshot. Uses local Ollama if reachable
+// (unmetered, $0); otherwise returns a deterministic grounded summary. No paid
+// API is called from here without Steve wiring it — cost line always shown.
+app.post('/api/chat', async (req, res) => {
+ const q = (req.body && req.body.q || '').toString().slice(0, 500);
+ if (!q) return res.status(400).json({ error: 'empty question' });
+ const ctx = JSON.stringify({ system: SNAP.system, fleet: { local: SNAP.localFleet, kamatera: SNAP.kamatera }, canaries: SNAP.canaries, cost: SNAP.cost, pnl: SNAP.pnl, wins: SNAP.wins }).slice(0, 6000);
+ const OLLAMA = process.env.OLLAMA_URL || 'http://127.0.0.1:11434';
+ const body = { model: process.env.OLLAMA_MODEL || 'qwen2.5:7b', stream: false, prompt: `You are AbramsEgo, Steve's fleet command center. Answer concisely using ONLY this live snapshot JSON:\n${ctx}\n\nQuestion: ${q}\nAnswer:` };
+ try {
+ const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(), 20000);
+ const r = await fetch(`${OLLAMA}/api/generate`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), signal: ctrl.signal });
+ clearTimeout(t);
+ if (r.ok) { const j = await r.json(); return res.json({ answer: (j.response || '').trim(), engine: 'ollama(local)', cost_usd: 0 }); }
+ } catch (e) {}
+ // deterministic fallback
+ const f = SNAP;
+ const answer = `Snapshot ${f.builtAt}. Local fleet ${f.localFleet && f.localFleet.up}/${f.localFleet && f.localFleet.total} up; Kamatera ${f.kamatera && f.kamatera.up}/${f.kamatera && f.kamatera.total} up. Cost today $${f.cost && f.cost.today && f.cost.today.total} (tokens $${f.cost && f.cost.today && f.cost.today.tokens} + energy $${f.cost && f.cost.today && f.cost.today.energy} est). Self-funding today: ${f.pnl && f.pnl.today && f.pnl.today.selfFundingPct}%. Wins today: ${f.wins && f.wins.today}.`;
+ res.json({ answer, engine: 'local-template', cost_usd: 0 });
+});
+
+app.use(express.static(path.join(__dirname, 'public'), { extensions: ['html'] }));
+
+app.listen(PORT, '127.0.0.1', () => {
+ console.log(`AbramsEgo v${VERSION} on http://127.0.0.1:${PORT} (auth: ${AUTH_USER})`);
+});
← 427fde2 auto-save: 2026-07-01T10:11:20 (4 files) — .deploy.conf .git
·
back to AbramsEgo
·
harness: restart-forever build loop (isolated queue + launch e8bcb58 →