habi-agent 0.1.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/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +295 -0
- package/dist/cli.js.map +1 -0
- package/dist/cloud.d.ts +50 -0
- package/dist/cloud.d.ts.map +1 -0
- package/dist/cloud.js +147 -0
- package/dist/cloud.js.map +1 -0
- package/dist/context.d.ts +62 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +172 -0
- package/dist/context.js.map +1 -0
- package/dist/hardware.d.ts +38 -0
- package/dist/hardware.d.ts.map +1 -0
- package/dist/hardware.js +130 -0
- package/dist/hardware.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/server.d.ts +25 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +276 -0
- package/dist/server.js.map +1 -0
- package/package.json +24 -0
- package/src/cli.ts +264 -0
- package/src/cloud.ts +165 -0
- package/src/context.ts +186 -0
- package/src/hardware.ts +114 -0
- package/src/index.ts +5 -0
- package/src/server.ts +255 -0
- package/tsconfig.json +10 -0
package/dist/context.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HABI Project Context File Loader
|
|
4
|
+
*
|
|
5
|
+
* The Project Context File is the source of truth for what is authorized
|
|
6
|
+
* on this machine for this project. It lives OUTSIDE version control.
|
|
7
|
+
*
|
|
8
|
+
* The SHA-256 hash of the file is computed on load and included in every
|
|
9
|
+
* session fingerprint. Tampering with the file invalidates active sessions.
|
|
10
|
+
*/
|
|
11
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
12
|
+
if (k2 === undefined) k2 = k;
|
|
13
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
14
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
15
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
16
|
+
}
|
|
17
|
+
Object.defineProperty(o, k2, desc);
|
|
18
|
+
}) : (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
o[k2] = m[k];
|
|
21
|
+
}));
|
|
22
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
23
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
24
|
+
}) : function(o, v) {
|
|
25
|
+
o["default"] = v;
|
|
26
|
+
});
|
|
27
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
28
|
+
var ownKeys = function(o) {
|
|
29
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
30
|
+
var ar = [];
|
|
31
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
32
|
+
return ar;
|
|
33
|
+
};
|
|
34
|
+
return ownKeys(o);
|
|
35
|
+
};
|
|
36
|
+
return function (mod) {
|
|
37
|
+
if (mod && mod.__esModule) return mod;
|
|
38
|
+
var result = {};
|
|
39
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
40
|
+
__setModuleDefault(result, mod);
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
})();
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.findContextFile = findContextFile;
|
|
46
|
+
exports.loadContext = loadContext;
|
|
47
|
+
exports.assertOperation = assertOperation;
|
|
48
|
+
exports.assertMachineAuthorized = assertMachineAuthorized;
|
|
49
|
+
const fs = __importStar(require("fs"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const crypto = __importStar(require("crypto"));
|
|
52
|
+
const CONTEXT_FILE_NAMES = [
|
|
53
|
+
'habi-context.json',
|
|
54
|
+
'.habi-context.json',
|
|
55
|
+
'habi.context.json',
|
|
56
|
+
];
|
|
57
|
+
/**
|
|
58
|
+
* Locates the context file by walking up from cwd.
|
|
59
|
+
* Searches: cwd → parent → grandparent → $HOME
|
|
60
|
+
*/
|
|
61
|
+
function findContextFile(startDir = process.cwd()) {
|
|
62
|
+
let dir = path.resolve(startDir);
|
|
63
|
+
const home = os_homedir();
|
|
64
|
+
while (true) {
|
|
65
|
+
for (const name of CONTEXT_FILE_NAMES) {
|
|
66
|
+
const candidate = path.join(dir, name);
|
|
67
|
+
if (fs.existsSync(candidate))
|
|
68
|
+
return candidate;
|
|
69
|
+
}
|
|
70
|
+
const parent = path.dirname(dir);
|
|
71
|
+
if (parent === dir)
|
|
72
|
+
break; // filesystem root
|
|
73
|
+
if (dir === home)
|
|
74
|
+
break; // don't go above home
|
|
75
|
+
dir = parent;
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
function os_homedir() {
|
|
80
|
+
return process.env.HOME || process.env.USERPROFILE || '/';
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Loads and validates a Project Context File.
|
|
84
|
+
*
|
|
85
|
+
* Validation:
|
|
86
|
+
* 1. File exists and is valid JSON
|
|
87
|
+
* 2. Required fields are present
|
|
88
|
+
* 3. Recomputed hash matches stored contextHash
|
|
89
|
+
* (if mismatch → file was tampered with → reject)
|
|
90
|
+
*/
|
|
91
|
+
function loadContext(filePath) {
|
|
92
|
+
if (!fs.existsSync(filePath)) {
|
|
93
|
+
throw new Error(`HABI: Context file not found: ${filePath}`);
|
|
94
|
+
}
|
|
95
|
+
const raw = fs.readFileSync(filePath, 'utf8');
|
|
96
|
+
let context;
|
|
97
|
+
try {
|
|
98
|
+
context = JSON.parse(raw);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
throw new Error(`HABI: Context file is not valid JSON: ${filePath}`);
|
|
102
|
+
}
|
|
103
|
+
// Validate required fields
|
|
104
|
+
const required = ['project', 'version', 'owner', 'allowedMachines', 'allowedOperations', 'deniedOperations'];
|
|
105
|
+
for (const field of required) {
|
|
106
|
+
if (!(field in context)) {
|
|
107
|
+
throw new Error(`HABI: Context file missing required field: ${field}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Compute hash of file contents excluding the contextHash field itself
|
|
111
|
+
const { contextHash: _stored, ...rest } = context;
|
|
112
|
+
const computedHash = crypto
|
|
113
|
+
.createHash('sha256')
|
|
114
|
+
.update(JSON.stringify(rest, null, 2))
|
|
115
|
+
.digest('hex');
|
|
116
|
+
// If contextHash is set, verify it matches
|
|
117
|
+
if (_stored && _stored !== computedHash) {
|
|
118
|
+
throw new Error(`HABI: Context file integrity check FAILED.\n` +
|
|
119
|
+
` Stored hash: ${_stored}\n` +
|
|
120
|
+
` Computed hash: ${computedHash}\n` +
|
|
121
|
+
` File may have been tampered with: ${filePath}`);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
context,
|
|
125
|
+
contextHash: computedHash,
|
|
126
|
+
filePath,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Checks whether a proposed operation is authorized under the loaded context.
|
|
131
|
+
*
|
|
132
|
+
* Returns: { allowed: true } or { allowed: false, reason: string }
|
|
133
|
+
*/
|
|
134
|
+
function assertOperation(operation, context) {
|
|
135
|
+
const opLower = operation.toLowerCase();
|
|
136
|
+
// ── Check denied list first (deny wins) ────────────────────────────────────
|
|
137
|
+
for (const denied of context.deniedOperations) {
|
|
138
|
+
if (opLower.includes(denied.toLowerCase())) {
|
|
139
|
+
return {
|
|
140
|
+
allowed: false,
|
|
141
|
+
reason: `Operation matches denied pattern: "${denied}"`,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
// ── Check allowed paths ─────────────────────────────────────────────────────
|
|
146
|
+
const ops = context.allowedOperations;
|
|
147
|
+
if (ops.commands && ops.commands.length > 0) {
|
|
148
|
+
const commandAllowed = ops.commands.some(cmd => opLower.startsWith(cmd.toLowerCase()));
|
|
149
|
+
if (!commandAllowed) {
|
|
150
|
+
return {
|
|
151
|
+
allowed: false,
|
|
152
|
+
reason: `Operation not in allowedOperations.commands list`,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { allowed: true };
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Checks whether this machine's hardware ID is authorized in the context file.
|
|
160
|
+
*/
|
|
161
|
+
function assertMachineAuthorized(hardwareId, context) {
|
|
162
|
+
const allowed = context.allowedMachines.some(m => m.toUpperCase() === hardwareId.toUpperCase());
|
|
163
|
+
if (!allowed) {
|
|
164
|
+
return {
|
|
165
|
+
allowed: false,
|
|
166
|
+
reason: `Hardware ID ${hardwareId} is not in allowedMachines list. ` +
|
|
167
|
+
`Authorized machines: ${context.allowedMachines.join(', ')}`,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
return { allowed: true };
|
|
171
|
+
}
|
|
172
|
+
//# sourceMappingURL=context.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HABI Hardware Identity Reader
|
|
3
|
+
* Reads MAC address (physical) or IPv6 link-local (container/VM)
|
|
4
|
+
* This is the foundation primitive of the entire HABI system.
|
|
5
|
+
*
|
|
6
|
+
* On physical machines: returns MAC via os.networkInterfaces()
|
|
7
|
+
* On containers/VMs: returns IPv6 link-local (fe80::/10) — auto-generated
|
|
8
|
+
* per virtual NIC, stable for container lifetime,
|
|
9
|
+
* non-routable externally
|
|
10
|
+
*/
|
|
11
|
+
export type FingerprintType = 'mac' | 'ipv6_link_local';
|
|
12
|
+
export interface HardwareIdentity {
|
|
13
|
+
fingerprint: string;
|
|
14
|
+
type: FingerprintType;
|
|
15
|
+
iface: string;
|
|
16
|
+
platform: string;
|
|
17
|
+
hostname: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Reads network interfaces and returns the primary hardware identifier.
|
|
21
|
+
* Priority: real MAC → IPv6 link-local → error
|
|
22
|
+
*
|
|
23
|
+
* Excludes loopback and virtual/docker interfaces.
|
|
24
|
+
* Returns the most stable, unique identifier available on this machine.
|
|
25
|
+
*/
|
|
26
|
+
export declare function readHardwareIdentity(): HardwareIdentity;
|
|
27
|
+
/**
|
|
28
|
+
* Generates a session fingerprint from hardware identity + context hash + timestamp.
|
|
29
|
+
* This fingerprint is signed into every audit log entry.
|
|
30
|
+
*
|
|
31
|
+
* fingerprint = SHA-256(hardware_id + ":" + context_hash + ":" + timestamp_ms)
|
|
32
|
+
*/
|
|
33
|
+
export declare function generateSessionFingerprint(hardwareId: string, contextHash: string, timestampMs?: number): string;
|
|
34
|
+
/**
|
|
35
|
+
* Computes SHA-256 of arbitrary input — used for operation hashing in audit log.
|
|
36
|
+
*/
|
|
37
|
+
export declare function sha256(input: string): string;
|
|
38
|
+
//# sourceMappingURL=hardware.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardware.d.ts","sourceRoot":"","sources":["../src/hardware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAKH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,iBAAiB,CAAC;AAExD,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,eAAe,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,oBAAoB,IAAI,gBAAgB,CA0DvD;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,GAAE,MAAmB,GAC/B,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAE5C"}
|
package/dist/hardware.js
ADDED
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* HABI Hardware Identity Reader
|
|
4
|
+
* Reads MAC address (physical) or IPv6 link-local (container/VM)
|
|
5
|
+
* This is the foundation primitive of the entire HABI system.
|
|
6
|
+
*
|
|
7
|
+
* On physical machines: returns MAC via os.networkInterfaces()
|
|
8
|
+
* On containers/VMs: returns IPv6 link-local (fe80::/10) — auto-generated
|
|
9
|
+
* per virtual NIC, stable for container lifetime,
|
|
10
|
+
* non-routable externally
|
|
11
|
+
*/
|
|
12
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13
|
+
if (k2 === undefined) k2 = k;
|
|
14
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
15
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
16
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
17
|
+
}
|
|
18
|
+
Object.defineProperty(o, k2, desc);
|
|
19
|
+
}) : (function(o, m, k, k2) {
|
|
20
|
+
if (k2 === undefined) k2 = k;
|
|
21
|
+
o[k2] = m[k];
|
|
22
|
+
}));
|
|
23
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
24
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
25
|
+
}) : function(o, v) {
|
|
26
|
+
o["default"] = v;
|
|
27
|
+
});
|
|
28
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
29
|
+
var ownKeys = function(o) {
|
|
30
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
31
|
+
var ar = [];
|
|
32
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
33
|
+
return ar;
|
|
34
|
+
};
|
|
35
|
+
return ownKeys(o);
|
|
36
|
+
};
|
|
37
|
+
return function (mod) {
|
|
38
|
+
if (mod && mod.__esModule) return mod;
|
|
39
|
+
var result = {};
|
|
40
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
41
|
+
__setModuleDefault(result, mod);
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
})();
|
|
45
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
|
+
exports.readHardwareIdentity = readHardwareIdentity;
|
|
47
|
+
exports.generateSessionFingerprint = generateSessionFingerprint;
|
|
48
|
+
exports.sha256 = sha256;
|
|
49
|
+
const os = __importStar(require("os"));
|
|
50
|
+
const crypto = __importStar(require("crypto"));
|
|
51
|
+
/**
|
|
52
|
+
* Reads network interfaces and returns the primary hardware identifier.
|
|
53
|
+
* Priority: real MAC → IPv6 link-local → error
|
|
54
|
+
*
|
|
55
|
+
* Excludes loopback and virtual/docker interfaces.
|
|
56
|
+
* Returns the most stable, unique identifier available on this machine.
|
|
57
|
+
*/
|
|
58
|
+
function readHardwareIdentity() {
|
|
59
|
+
const ifaces = os.networkInterfaces();
|
|
60
|
+
const platform = os.platform();
|
|
61
|
+
const hostname = os.hostname();
|
|
62
|
+
// ── Pass 1: Find a real MAC address ────────────────────────────────────────
|
|
63
|
+
// Exclude loopback (lo, lo0) and known virtual prefixes
|
|
64
|
+
const VIRTUAL_PREFIXES = ['docker', 'br-', 'veth', 'virbr', 'vmnet', 'vbox'];
|
|
65
|
+
for (const [name, addrs] of Object.entries(ifaces)) {
|
|
66
|
+
if (!addrs)
|
|
67
|
+
continue;
|
|
68
|
+
// Skip loopback
|
|
69
|
+
if (addrs.some(a => a.internal))
|
|
70
|
+
continue;
|
|
71
|
+
// Skip known virtual interfaces
|
|
72
|
+
const ifaceLower = name.toLowerCase();
|
|
73
|
+
if (VIRTUAL_PREFIXES.some(prefix => ifaceLower.startsWith(prefix)))
|
|
74
|
+
continue;
|
|
75
|
+
for (const addr of addrs) {
|
|
76
|
+
const mac = addr.mac;
|
|
77
|
+
// Validate: real MAC is not all-zeros, not broadcast
|
|
78
|
+
if (mac && mac !== '00:00:00:00:00:00' && mac !== 'ff:ff:ff:ff:ff:ff') {
|
|
79
|
+
return {
|
|
80
|
+
fingerprint: mac.toUpperCase(),
|
|
81
|
+
type: 'mac',
|
|
82
|
+
iface: name,
|
|
83
|
+
platform,
|
|
84
|
+
hostname,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ── Pass 2: IPv6 link-local (fe80::/10) — container/VM fallback ───────────
|
|
90
|
+
for (const [name, addrs] of Object.entries(ifaces)) {
|
|
91
|
+
if (!addrs)
|
|
92
|
+
continue;
|
|
93
|
+
if (addrs.some(a => a.internal))
|
|
94
|
+
continue;
|
|
95
|
+
for (const addr of addrs) {
|
|
96
|
+
if (addr.family === 'IPv6' && addr.address.toLowerCase().startsWith('fe80')) {
|
|
97
|
+
return {
|
|
98
|
+
fingerprint: addr.address.toLowerCase(),
|
|
99
|
+
type: 'ipv6_link_local',
|
|
100
|
+
iface: name,
|
|
101
|
+
platform,
|
|
102
|
+
hostname,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ── Fail loudly — never silently return a fake identity ───────────────────
|
|
108
|
+
throw new Error('HABI: No stable hardware identifier found. ' +
|
|
109
|
+
'Physical machines require a non-virtual network interface with a MAC address. ' +
|
|
110
|
+
'Containers require an IPv6 link-local address (fe80::/10).');
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Generates a session fingerprint from hardware identity + context hash + timestamp.
|
|
114
|
+
* This fingerprint is signed into every audit log entry.
|
|
115
|
+
*
|
|
116
|
+
* fingerprint = SHA-256(hardware_id + ":" + context_hash + ":" + timestamp_ms)
|
|
117
|
+
*/
|
|
118
|
+
function generateSessionFingerprint(hardwareId, contextHash, timestampMs = Date.now()) {
|
|
119
|
+
return crypto
|
|
120
|
+
.createHash('sha256')
|
|
121
|
+
.update(`${hardwareId}:${contextHash}:${timestampMs}`)
|
|
122
|
+
.digest('hex');
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Computes SHA-256 of arbitrary input — used for operation hashing in audit log.
|
|
126
|
+
*/
|
|
127
|
+
function sha256(input) {
|
|
128
|
+
return crypto.createHash('sha256').update(input).digest('hex');
|
|
129
|
+
}
|
|
130
|
+
//# sourceMappingURL=hardware.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hardware.js","sourceRoot":"","sources":["../src/hardware.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { HabiLocalServer } from './server';
|
|
2
|
+
export { readHardwareIdentity, generateSessionFingerprint, sha256 } from './hardware';
|
|
3
|
+
export { findContextFile, loadContext, assertOperation, assertMachineAuthorized } from './context';
|
|
4
|
+
export type { HardwareIdentity, FingerprintType } from './hardware';
|
|
5
|
+
export type { ProjectContext, LoadedContext, AllowedOperations } from './context';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAC3C,OAAO,EAAE,oBAAoB,EAAE,0BAA0B,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACtF,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,WAAW,CAAC;AACnG,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AACpE,YAAY,EAAE,cAAc,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.assertMachineAuthorized = exports.assertOperation = exports.loadContext = exports.findContextFile = exports.sha256 = exports.generateSessionFingerprint = exports.readHardwareIdentity = exports.HabiLocalServer = void 0;
|
|
4
|
+
var server_1 = require("./server");
|
|
5
|
+
Object.defineProperty(exports, "HabiLocalServer", { enumerable: true, get: function () { return server_1.HabiLocalServer; } });
|
|
6
|
+
var hardware_1 = require("./hardware");
|
|
7
|
+
Object.defineProperty(exports, "readHardwareIdentity", { enumerable: true, get: function () { return hardware_1.readHardwareIdentity; } });
|
|
8
|
+
Object.defineProperty(exports, "generateSessionFingerprint", { enumerable: true, get: function () { return hardware_1.generateSessionFingerprint; } });
|
|
9
|
+
Object.defineProperty(exports, "sha256", { enumerable: true, get: function () { return hardware_1.sha256; } });
|
|
10
|
+
var context_1 = require("./context");
|
|
11
|
+
Object.defineProperty(exports, "findContextFile", { enumerable: true, get: function () { return context_1.findContextFile; } });
|
|
12
|
+
Object.defineProperty(exports, "loadContext", { enumerable: true, get: function () { return context_1.loadContext; } });
|
|
13
|
+
Object.defineProperty(exports, "assertOperation", { enumerable: true, get: function () { return context_1.assertOperation; } });
|
|
14
|
+
Object.defineProperty(exports, "assertMachineAuthorized", { enumerable: true, get: function () { return context_1.assertMachineAuthorized; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,mCAA2C;AAAlC,yGAAA,eAAe,OAAA;AACxB,uCAAsF;AAA7E,gHAAA,oBAAoB,OAAA;AAAE,sHAAA,0BAA0B,OAAA;AAAE,kGAAA,MAAM,OAAA;AACjE,qCAAmG;AAA1F,0GAAA,eAAe,OAAA;AAAE,sGAAA,WAAW,OAAA;AAAE,0GAAA,eAAe,OAAA;AAAE,kHAAA,uBAAuB,OAAA"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HABI Local Identity Server
|
|
3
|
+
* Runs on localhost:7432 exclusively — never binds to 0.0.0.0
|
|
4
|
+
*
|
|
5
|
+
* Pure Node.js http — zero external dependencies.
|
|
6
|
+
* Security: localhost-only binding is the enforced hardware boundary.
|
|
7
|
+
*/
|
|
8
|
+
export declare class HabiLocalServer {
|
|
9
|
+
private hardware;
|
|
10
|
+
private context;
|
|
11
|
+
private session;
|
|
12
|
+
private server;
|
|
13
|
+
private isLocalhost;
|
|
14
|
+
private handleHealth;
|
|
15
|
+
private handleIdentity;
|
|
16
|
+
private handleVerify;
|
|
17
|
+
private handleAssertContext;
|
|
18
|
+
private handleAudit;
|
|
19
|
+
private getHardware;
|
|
20
|
+
private getContext;
|
|
21
|
+
private getOrCreateSession;
|
|
22
|
+
start(): Promise<void>;
|
|
23
|
+
stop(): void;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAoCH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAiC;IACjD,OAAO,CAAC,OAAO,CAA8B;IAC7C,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,MAAM,CAA4B;IAG1C,OAAO,CAAC,WAAW;IAMnB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,YAAY;YAwBN,mBAAmB;YAiCnB,WAAW;IAmBzB,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,kBAAkB;IAqB1B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAiDtB,IAAI,IAAI,IAAI;CAIb"}
|