@floatonnear/float-adapter 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/env-file.d.ts +45 -0
- package/dist/env-file.js +82 -0
- package/dist/server.js +25 -0
- package/package.json +9 -3
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read an env file without involving a shell.
|
|
3
|
+
*
|
|
4
|
+
* Every doc in this repo tells the operator to run:
|
|
5
|
+
*
|
|
6
|
+
* set -a && . .float/adapter.env && set +a && npx @floatonnear/float-adapter
|
|
7
|
+
*
|
|
8
|
+
* That idiom has already failed on a real box. From this project's memory
|
|
9
|
+
* (prod-env-file-cannot-be-sourced-by-bash): one unquoted value containing a
|
|
10
|
+
* space made `set -a; . .env; set +a` die with `Scan: command not found`, and
|
|
11
|
+
* every script opening with that line failed before it ran.
|
|
12
|
+
*
|
|
13
|
+
* Two reasons that is the wrong mechanism HERE specifically, rather than merely
|
|
14
|
+
* fragile:
|
|
15
|
+
*
|
|
16
|
+
* 1. The file is written by create-float and carries NEAR_VAULT_AGENT_KEY,
|
|
17
|
+
* FLOAT_QUOTE_SIGNER_KEY and FLOAT_API_TOKEN. Sourcing it means the shell
|
|
18
|
+
* EXECUTES its contents: `$(...)` and backticks in a value run as
|
|
19
|
+
* commands. A secrets file should be data.
|
|
20
|
+
* 2. It has to work the same way on Windows and on a VPS. `set -a` is not
|
|
21
|
+
* available in PowerShell at all, which is why this repo's memory carries
|
|
22
|
+
* a bespoke `Get-Content | ForEach-Object { Set-Item Env: }` incantation
|
|
23
|
+
* just to start the adapter locally.
|
|
24
|
+
*
|
|
25
|
+
* One parser, tested, used by both.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Parse `KEY=value` lines into a plain object.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately NOT a dotenv clone: no variable interpolation, no escape
|
|
31
|
+
* sequences, no multi-line values. Each of those is a way for the contents of a
|
|
32
|
+
* secrets file to change meaning, and none is needed by the file create-float
|
|
33
|
+
* writes.
|
|
34
|
+
*/
|
|
35
|
+
export declare function parseEnvFile(text: string): Record<string, string>;
|
|
36
|
+
/**
|
|
37
|
+
* Apply a parsed env file WITHOUT overriding anything already set.
|
|
38
|
+
*
|
|
39
|
+
* A real environment variable must outrank the file, so a deploy can point one
|
|
40
|
+
* service at a different port or contract without editing shared secrets. That
|
|
41
|
+
* precedence is the same one the pythscan sandbox relied on to keep a verify run
|
|
42
|
+
* aimed at a database COPY while every credential still came from the
|
|
43
|
+
* production file.
|
|
44
|
+
*/
|
|
45
|
+
export declare function applyEnvFile(parsed: Record<string, string>, env?: Record<string, string | undefined>): string[];
|
package/dist/env-file.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read an env file without involving a shell.
|
|
3
|
+
*
|
|
4
|
+
* Every doc in this repo tells the operator to run:
|
|
5
|
+
*
|
|
6
|
+
* set -a && . .float/adapter.env && set +a && npx @floatonnear/float-adapter
|
|
7
|
+
*
|
|
8
|
+
* That idiom has already failed on a real box. From this project's memory
|
|
9
|
+
* (prod-env-file-cannot-be-sourced-by-bash): one unquoted value containing a
|
|
10
|
+
* space made `set -a; . .env; set +a` die with `Scan: command not found`, and
|
|
11
|
+
* every script opening with that line failed before it ran.
|
|
12
|
+
*
|
|
13
|
+
* Two reasons that is the wrong mechanism HERE specifically, rather than merely
|
|
14
|
+
* fragile:
|
|
15
|
+
*
|
|
16
|
+
* 1. The file is written by create-float and carries NEAR_VAULT_AGENT_KEY,
|
|
17
|
+
* FLOAT_QUOTE_SIGNER_KEY and FLOAT_API_TOKEN. Sourcing it means the shell
|
|
18
|
+
* EXECUTES its contents: `$(...)` and backticks in a value run as
|
|
19
|
+
* commands. A secrets file should be data.
|
|
20
|
+
* 2. It has to work the same way on Windows and on a VPS. `set -a` is not
|
|
21
|
+
* available in PowerShell at all, which is why this repo's memory carries
|
|
22
|
+
* a bespoke `Get-Content | ForEach-Object { Set-Item Env: }` incantation
|
|
23
|
+
* just to start the adapter locally.
|
|
24
|
+
*
|
|
25
|
+
* One parser, tested, used by both.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Parse `KEY=value` lines into a plain object.
|
|
29
|
+
*
|
|
30
|
+
* Deliberately NOT a dotenv clone: no variable interpolation, no escape
|
|
31
|
+
* sequences, no multi-line values. Each of those is a way for the contents of a
|
|
32
|
+
* secrets file to change meaning, and none is needed by the file create-float
|
|
33
|
+
* writes.
|
|
34
|
+
*/
|
|
35
|
+
export function parseEnvFile(text) {
|
|
36
|
+
const out = {};
|
|
37
|
+
// Strip a BOM. PowerShell's Out-File and Set-Content write one by default, so
|
|
38
|
+
// any file the operator has touched from a one-liner has it, and it would
|
|
39
|
+
// otherwise become part of the FIRST key's name — producing a missing-variable
|
|
40
|
+
// failure whose cause is invisible.
|
|
41
|
+
for (const raw of text.replace(/^/, "").split(/\r?\n/)) {
|
|
42
|
+
const line = raw.trim();
|
|
43
|
+
if (!line || line.startsWith("#"))
|
|
44
|
+
continue;
|
|
45
|
+
// Split on the FIRST '=' only. Base64url tokens and ed25519 keys contain
|
|
46
|
+
// '=' as padding, and splitting on each one silently truncates a secret —
|
|
47
|
+
// which surfaces as a 401 that nobody can account for.
|
|
48
|
+
const eq = line.indexOf("=");
|
|
49
|
+
if (eq < 0)
|
|
50
|
+
continue; // not an assignment; not our business
|
|
51
|
+
const key = line.slice(0, eq).trim().replace(/^export\s+/, "");
|
|
52
|
+
if (!key)
|
|
53
|
+
continue;
|
|
54
|
+
let value = line.slice(eq + 1).trim();
|
|
55
|
+
// Strip ONE layer of matching surrounding quotes, so both `A="x y"` and the
|
|
56
|
+
// bare `A=x y` that broke the shell mean the same thing here.
|
|
57
|
+
if (value.length >= 2 && ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'")))) {
|
|
58
|
+
value = value.slice(1, -1);
|
|
59
|
+
}
|
|
60
|
+
out[key] = value;
|
|
61
|
+
}
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Apply a parsed env file WITHOUT overriding anything already set.
|
|
66
|
+
*
|
|
67
|
+
* A real environment variable must outrank the file, so a deploy can point one
|
|
68
|
+
* service at a different port or contract without editing shared secrets. That
|
|
69
|
+
* precedence is the same one the pythscan sandbox relied on to keep a verify run
|
|
70
|
+
* aimed at a database COPY while every credential still came from the
|
|
71
|
+
* production file.
|
|
72
|
+
*/
|
|
73
|
+
export function applyEnvFile(parsed, env = process.env) {
|
|
74
|
+
const applied = [];
|
|
75
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
76
|
+
if (env[k] !== undefined)
|
|
77
|
+
continue;
|
|
78
|
+
env[k] = v;
|
|
79
|
+
applied.push(k);
|
|
80
|
+
}
|
|
81
|
+
return applied;
|
|
82
|
+
}
|
package/dist/server.js
CHANGED
|
@@ -28,6 +28,31 @@ import { planFreezeNode, planSetCooldown, planSetNodeCaps, planSetRecipients, to
|
|
|
28
28
|
import { getAgentKeys, getDashboard, getSpendHistory, getVaultInfo, paymentFromOutcome, } from "./handlers.js";
|
|
29
29
|
import { AgentSelectionError, loadKeyring, selectAgent } from "./keyring.js";
|
|
30
30
|
import { BlockedFollower, createRpc, emptyStore } from "./blocked-scan.js";
|
|
31
|
+
import { parseEnvFile, applyEnvFile } from "./env-file.js";
|
|
32
|
+
// Load FLOAT_ENV_FILE before anything reads process.env.
|
|
33
|
+
//
|
|
34
|
+
// This replaces `set -a && . .float/adapter.env && set +a` in every deploy
|
|
35
|
+
// path. That idiom makes the SHELL execute a file holding the agent key, the
|
|
36
|
+
// quote signer and the API token, and it has already failed for real on a single
|
|
37
|
+
// unquoted value containing a space (see env-file.ts). It also does not exist in
|
|
38
|
+
// PowerShell, which is why starting the adapter on Windows needed a bespoke
|
|
39
|
+
// Get-Content incantation recorded in this project's memory.
|
|
40
|
+
//
|
|
41
|
+
// Real environment variables win over the file, so one service can be pointed
|
|
42
|
+
// at a different port or contract without editing shared secrets.
|
|
43
|
+
if (process.env.FLOAT_ENV_FILE) {
|
|
44
|
+
const envPath = process.env.FLOAT_ENV_FILE;
|
|
45
|
+
if (!existsSync(envPath)) {
|
|
46
|
+
// Fail loudly. A missing env file that is silently ignored starts an adapter
|
|
47
|
+
// with no API token, which disables every mutating route and looks like a
|
|
48
|
+
// code bug rather than a deploy mistake.
|
|
49
|
+
console.error(`FATAL: FLOAT_ENV_FILE=${envPath} does not exist`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
const applied = applyEnvFile(parseEnvFile(readFileSync(envPath, "utf8")));
|
|
53
|
+
// NAMES only, never values: this line goes to pm2's log on every restart.
|
|
54
|
+
console.log(`env file: ${envPath} — set ${applied.length} (${applied.join(", ") || "none"})`);
|
|
55
|
+
}
|
|
31
56
|
const env = {
|
|
32
57
|
vault: process.env.NEAR_VAULT_CONTRACT ?? "",
|
|
33
58
|
// See DEFAULT_RPC_URL in @floatonnear/mcp: the old public testnet endpoint
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@floatonnear/float-adapter",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Serves the existing Float dashboard's /v1 API from NEAR vault views, so the Float UI runs unchanged against the NEAR hierarchical vault.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -17,6 +17,12 @@
|
|
|
17
17
|
"test": "vitest run",
|
|
18
18
|
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
19
19
|
},
|
|
20
|
-
"dependencies": {
|
|
21
|
-
|
|
20
|
+
"dependencies": {
|
|
21
|
+
"@floatonnear/mcp": "^0.1.0"
|
|
22
|
+
},
|
|
23
|
+
"devDependencies": {
|
|
24
|
+
"typescript": "^5.9.0",
|
|
25
|
+
"vitest": "^5.0.5",
|
|
26
|
+
"@types/node": "^24.0.0"
|
|
27
|
+
}
|
|
22
28
|
}
|