movehat 0.2.1 → 0.2.3
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/__tests__/deployContract.test.js +56 -47
- package/dist/__tests__/deployContract.test.js.map +1 -1
- package/dist/__tests__/exports.test.d.ts +2 -0
- package/dist/__tests__/exports.test.d.ts.map +1 -0
- package/dist/__tests__/exports.test.js +30 -0
- package/dist/__tests__/exports.test.js.map +1 -0
- package/dist/__tests__/fixtures/sigint-deploy-harness.d.ts +4 -3
- package/dist/__tests__/fixtures/sigint-deploy-harness.d.ts.map +1 -1
- package/dist/__tests__/fixtures/sigint-deploy-harness.js +8 -7
- package/dist/__tests__/fixtures/sigint-deploy-harness.js.map +1 -1
- package/dist/__tests__/fork/api.test.js +5 -0
- package/dist/__tests__/fork/api.test.js.map +1 -1
- package/dist/__tests__/fork/api.timeout.test.d.ts +2 -0
- package/dist/__tests__/fork/api.timeout.test.d.ts.map +1 -0
- package/dist/__tests__/fork/api.timeout.test.js +98 -0
- package/dist/__tests__/fork/api.timeout.test.js.map +1 -0
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/__tests__/compile.toml-mutation.test.d.ts +2 -0
- package/dist/commands/__tests__/compile.toml-mutation.test.d.ts.map +1 -0
- package/dist/commands/__tests__/compile.toml-mutation.test.js +69 -0
- package/dist/commands/__tests__/compile.toml-mutation.test.js.map +1 -0
- package/dist/commands/__tests__/init.test.js +73 -11
- package/dist/commands/__tests__/init.test.js.map +1 -1
- package/dist/commands/compile.d.ts.map +1 -1
- package/dist/commands/compile.js +19 -10
- package/dist/commands/compile.js.map +1 -1
- package/dist/commands/init.d.ts +22 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +55 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/test.js +12 -19
- package/dist/commands/test.js.map +1 -1
- package/dist/core/AccountManager.d.ts.map +1 -1
- package/dist/core/AccountManager.js +14 -2
- package/dist/core/AccountManager.js.map +1 -1
- package/dist/core/Publisher.d.ts.map +1 -1
- package/dist/core/Publisher.js +72 -82
- package/dist/core/Publisher.js.map +1 -1
- package/dist/core/__tests__/AccountManager.global-state.test.d.ts +2 -0
- package/dist/core/__tests__/AccountManager.global-state.test.d.ts.map +1 -0
- package/dist/core/__tests__/AccountManager.global-state.test.js +69 -0
- package/dist/core/__tests__/AccountManager.global-state.test.js.map +1 -0
- package/dist/core/__tests__/movementProfile.test.d.ts +2 -0
- package/dist/core/__tests__/movementProfile.test.d.ts.map +1 -0
- package/dist/core/__tests__/movementProfile.test.js +112 -0
- package/dist/core/__tests__/movementProfile.test.js.map +1 -0
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +14 -10
- package/dist/core/config.js.map +1 -1
- package/dist/core/deployments.d.ts.map +1 -1
- package/dist/core/deployments.js +4 -2
- package/dist/core/deployments.js.map +1 -1
- package/dist/core/movementProfile.d.ts +55 -22
- package/dist/core/movementProfile.d.ts.map +1 -1
- package/dist/core/movementProfile.js +77 -99
- package/dist/core/movementProfile.js.map +1 -1
- package/dist/fork/__tests__/server.cors.test.d.ts +2 -0
- package/dist/fork/__tests__/server.cors.test.d.ts.map +1 -0
- package/dist/fork/__tests__/server.cors.test.js +79 -0
- package/dist/fork/__tests__/server.cors.test.js.map +1 -0
- package/dist/fork/api.d.ts +9 -1
- package/dist/fork/api.d.ts.map +1 -1
- package/dist/fork/api.js +37 -7
- package/dist/fork/api.js.map +1 -1
- package/dist/fork/manager.js +10 -10
- package/dist/fork/manager.js.map +1 -1
- package/dist/fork/server.d.ts +20 -1
- package/dist/fork/server.d.ts.map +1 -1
- package/dist/fork/server.js +40 -24
- package/dist/fork/server.js.map +1 -1
- package/dist/fork/test.d.ts.map +1 -1
- package/dist/fork/test.js +3 -2
- package/dist/fork/test.js.map +1 -1
- package/dist/harness/Harness.d.ts +6 -2
- package/dist/harness/Harness.d.ts.map +1 -1
- package/dist/harness/Harness.js +8 -2
- package/dist/harness/Harness.js.map +1 -1
- package/dist/harness/codeObject.d.ts.map +1 -1
- package/dist/harness/codeObject.js +41 -41
- package/dist/harness/codeObject.js.map +1 -1
- package/dist/harness/script.d.ts +3 -3
- package/dist/harness/script.d.ts.map +1 -1
- package/dist/harness/script.js +42 -35
- package/dist/harness/script.js.map +1 -1
- package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.d.ts +2 -0
- package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.d.ts.map +1 -0
- package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.js +172 -0
- package/dist/helpers/__tests__/setupLocalTesting.fork-network.test.js.map +1 -0
- package/dist/helpers/setupLocalTesting.d.ts.map +1 -1
- package/dist/helpers/setupLocalTesting.js +31 -5
- package/dist/helpers/setupLocalTesting.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/node/LocalNodeManager.d.ts +8 -0
- package/dist/node/LocalNodeManager.d.ts.map +1 -1
- package/dist/node/LocalNodeManager.js +70 -23
- package/dist/node/LocalNodeManager.js.map +1 -1
- package/dist/node/__tests__/LocalNodeManager.api-port.test.d.ts +2 -0
- package/dist/node/__tests__/LocalNodeManager.api-port.test.d.ts.map +1 -0
- package/dist/node/__tests__/LocalNodeManager.api-port.test.js +55 -0
- package/dist/node/__tests__/LocalNodeManager.api-port.test.js.map +1 -0
- package/dist/node/__tests__/LocalNodeManager.test.js +114 -14
- package/dist/node/__tests__/LocalNodeManager.test.js.map +1 -1
- package/dist/templates/move/Move.toml +1 -1
- package/dist/templates/move/sources/Counter.move +31 -4
- package/dist/templates/scripts/deploy-counter.ts +10 -0
- package/dist/types/config.d.ts +8 -1
- package/dist/types/config.d.ts.map +1 -1
- package/dist/ui/__tests__/logger.test.d.ts +2 -0
- package/dist/ui/__tests__/logger.test.d.ts.map +1 -0
- package/dist/ui/__tests__/logger.test.js +75 -0
- package/dist/ui/__tests__/logger.test.js.map +1 -0
- package/dist/ui/formatters.d.ts +0 -16
- package/dist/ui/formatters.d.ts.map +1 -1
- package/dist/ui/formatters.js +1 -1
- package/dist/ui/formatters.js.map +1 -1
- package/dist/ui/logger.d.ts +41 -0
- package/dist/ui/logger.d.ts.map +1 -1
- package/dist/ui/logger.js +49 -0
- package/dist/ui/logger.js.map +1 -1
- package/dist/ui/spinner.d.ts +25 -0
- package/dist/ui/spinner.d.ts.map +1 -1
- package/dist/ui/spinner.js +44 -0
- package/dist/ui/spinner.js.map +1 -1
- package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.d.ts +2 -0
- package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.d.ts.map +1 -0
- package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.js +43 -0
- package/dist/utils/__tests__/childProcessAdapter.maxBuffer.test.js.map +1 -0
- package/dist/utils/childProcessAdapter.d.ts +7 -0
- package/dist/utils/childProcessAdapter.d.ts.map +1 -1
- package/dist/utils/childProcessAdapter.js +20 -2
- package/dist/utils/childProcessAdapter.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/deployContract.test.ts +59 -50
- package/src/__tests__/exports.test.ts +32 -0
- package/src/__tests__/fixtures/sigint-deploy-harness.ts +8 -7
- package/src/__tests__/fork/api.test.ts +5 -0
- package/src/__tests__/fork/api.timeout.test.ts +150 -0
- package/src/cli.ts +4 -0
- package/src/commands/__tests__/compile.toml-mutation.test.ts +77 -0
- package/src/commands/__tests__/init.test.ts +96 -11
- package/src/commands/compile.ts +24 -15
- package/src/commands/init.ts +77 -6
- package/src/commands/test.ts +12 -19
- package/src/core/AccountManager.ts +18 -1
- package/src/core/Publisher.ts +103 -107
- package/src/core/__tests__/AccountManager.global-state.test.ts +83 -0
- package/src/core/__tests__/movementProfile.test.ts +131 -0
- package/src/core/config.ts +18 -11
- package/src/core/deployments.ts +5 -4
- package/src/core/movementProfile.ts +75 -127
- package/src/fork/__tests__/server.cors.test.ts +101 -0
- package/src/fork/api.ts +69 -10
- package/src/fork/manager.ts +10 -10
- package/src/fork/server.ts +59 -24
- package/src/fork/test.ts +3 -2
- package/src/harness/Harness.ts +11 -2
- package/src/harness/codeObject.ts +45 -48
- package/src/harness/script.ts +47 -43
- package/src/helpers/__tests__/setupLocalTesting.fork-network.test.ts +212 -0
- package/src/helpers/setupLocalTesting.ts +39 -5
- package/src/index.ts +9 -1
- package/src/node/LocalNodeManager.ts +87 -26
- package/src/node/__tests__/LocalNodeManager.api-port.test.ts +62 -0
- package/src/node/__tests__/LocalNodeManager.test.ts +144 -17
- package/src/templates/move/Move.toml +1 -1
- package/src/templates/move/sources/Counter.move +31 -4
- package/src/templates/scripts/deploy-counter.ts +10 -0
- package/src/types/config.ts +8 -1
- package/src/ui/__tests__/logger.test.ts +89 -0
- package/src/ui/formatters.ts +1 -1
- package/src/ui/logger.ts +62 -0
- package/src/ui/spinner.ts +47 -0
- package/src/utils/__tests__/childProcessAdapter.maxBuffer.test.ts +51 -0
- package/src/utils/childProcessAdapter.ts +32 -2
package/src/fork/server.ts
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
import http from 'http';
|
|
2
2
|
import { URL } from 'url';
|
|
3
3
|
import { ForkManager } from './manager.js';
|
|
4
|
+
import { logger } from '../ui/index.js';
|
|
5
|
+
|
|
6
|
+
export interface ForkServerOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Origins allowed to make cross-origin requests. When unset (default),
|
|
9
|
+
* no `Access-Control-Allow-Origin` header is emitted — any browser
|
|
10
|
+
* cross-origin read is rejected by the user agent. Setting this to a
|
|
11
|
+
* non-empty list opts into echoing matching `Origin` request headers
|
|
12
|
+
* back. Wildcard `'*'` is intentionally NOT supported: cached fork
|
|
13
|
+
* state may include resources that should not be readable by every
|
|
14
|
+
* page in the dev's browser.
|
|
15
|
+
*/
|
|
16
|
+
corsAllowOrigins?: readonly string[];
|
|
17
|
+
}
|
|
4
18
|
|
|
5
19
|
/**
|
|
6
20
|
* Fork Server - Serves fork data via Movement L1 RPC API
|
|
@@ -11,16 +25,38 @@ export class ForkServer {
|
|
|
11
25
|
private forkManager: ForkManager;
|
|
12
26
|
private port: number;
|
|
13
27
|
private host: string;
|
|
28
|
+
private readonly corsAllowOrigins: ReadonlySet<string>;
|
|
14
29
|
|
|
15
30
|
/**
|
|
16
31
|
* @param host Interface to bind. Defaults to `127.0.0.1` so cached fork
|
|
17
32
|
* state (which may include sensitive resources) is not exposed on the LAN.
|
|
18
33
|
* Pass `'0.0.0.0'` only if you intentionally need to expose the server.
|
|
34
|
+
* @param options Optional CORS allowlist (see {@link ForkServerOptions}).
|
|
19
35
|
*/
|
|
20
|
-
constructor(
|
|
36
|
+
constructor(
|
|
37
|
+
forkPath: string,
|
|
38
|
+
port: number = 8080,
|
|
39
|
+
host: string = '127.0.0.1',
|
|
40
|
+
options: ForkServerOptions = {}
|
|
41
|
+
) {
|
|
21
42
|
this.forkManager = new ForkManager(forkPath);
|
|
22
43
|
this.port = port;
|
|
23
44
|
this.host = host;
|
|
45
|
+
this.corsAllowOrigins = new Set(options.corsAllowOrigins ?? []);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set CORS headers for a request when the request's `Origin` is in
|
|
50
|
+
* the allowlist. No-op otherwise.
|
|
51
|
+
*/
|
|
52
|
+
private applyCors(req: http.IncomingMessage, res: http.ServerResponse): void {
|
|
53
|
+
const origin = req.headers.origin;
|
|
54
|
+
if (typeof origin === 'string' && this.corsAllowOrigins.has(origin)) {
|
|
55
|
+
res.setHeader('Access-Control-Allow-Origin', origin);
|
|
56
|
+
res.setHeader('Vary', 'Origin');
|
|
57
|
+
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
58
|
+
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
59
|
+
}
|
|
24
60
|
}
|
|
25
61
|
|
|
26
62
|
/**
|
|
@@ -31,23 +67,21 @@ export class ForkServer {
|
|
|
31
67
|
this.forkManager.load();
|
|
32
68
|
const metadata = this.forkManager.getMetadata();
|
|
33
69
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
70
|
+
logger.newline();
|
|
71
|
+
logger.phase("Fork Server");
|
|
72
|
+
logger.kv("Network", metadata.network, 2);
|
|
73
|
+
logger.kv("Chain ID", String(metadata.chainId), 2);
|
|
74
|
+
logger.kv("Ledger Version", String(metadata.ledgerVersion), 2);
|
|
75
|
+
logger.kv("Forked at", metadata.createdAt, 2);
|
|
39
76
|
|
|
40
77
|
this.server = http.createServer((req, res) => {
|
|
41
78
|
this.handleRequest(req, res).catch((error) => {
|
|
42
79
|
// Log full error server-side for diagnostics
|
|
43
|
-
|
|
80
|
+
logger.error(`Error handling request: ${error instanceof Error ? error.message : String(error)}`);
|
|
44
81
|
|
|
45
82
|
// Only send response if headers haven't been sent yet
|
|
46
83
|
if (!res.headersSent) {
|
|
47
|
-
|
|
48
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
49
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
50
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
84
|
+
this.applyCors(req, res);
|
|
51
85
|
|
|
52
86
|
// Send generic error response (no internal details exposed)
|
|
53
87
|
this.sendJSON(res, 500, {
|
|
@@ -92,13 +126,15 @@ export class ForkServer {
|
|
|
92
126
|
: isIpv6
|
|
93
127
|
? `[${this.host}]`
|
|
94
128
|
: this.host;
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
129
|
+
logger.newline();
|
|
130
|
+
logger.success(`Fork Server listening on http://${displayHost}:${this.port}`);
|
|
131
|
+
logger.kv("Bound interface", this.host, 2);
|
|
132
|
+
logger.kv("Ledger Info", `http://${displayHost}:${this.port}/v1/`, 2);
|
|
98
133
|
if (this.host === '0.0.0.0') {
|
|
99
|
-
|
|
134
|
+
logger.warning("Server is bound to 0.0.0.0 — fork state is reachable from the LAN.", 2);
|
|
100
135
|
}
|
|
101
|
-
|
|
136
|
+
logger.newline();
|
|
137
|
+
logger.info("Press Ctrl+C to stop");
|
|
102
138
|
resolve();
|
|
103
139
|
});
|
|
104
140
|
});
|
|
@@ -111,7 +147,8 @@ export class ForkServer {
|
|
|
111
147
|
return new Promise((resolve) => {
|
|
112
148
|
if (this.server) {
|
|
113
149
|
this.server.close(() => {
|
|
114
|
-
|
|
150
|
+
logger.newline();
|
|
151
|
+
logger.success("Fork Server stopped");
|
|
115
152
|
resolve();
|
|
116
153
|
});
|
|
117
154
|
} else {
|
|
@@ -140,13 +177,11 @@ export class ForkServer {
|
|
|
140
177
|
const url = new URL(req.url || '/', `http://localhost:${this.port}`);
|
|
141
178
|
const pathname = url.pathname;
|
|
142
179
|
|
|
143
|
-
// Log request
|
|
144
|
-
|
|
180
|
+
// Log request — plain so the fork-server access log retains its
|
|
181
|
+
// grep-friendly line shape (timestamp + method + path, no symbol).
|
|
182
|
+
logger.plain(`[${new Date().toISOString()}] ${req.method} ${pathname}`);
|
|
145
183
|
|
|
146
|
-
|
|
147
|
-
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
148
|
-
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
149
|
-
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
|
|
184
|
+
this.applyCors(req, res);
|
|
150
185
|
|
|
151
186
|
// Handle OPTIONS for CORS preflight
|
|
152
187
|
if (req.method === 'OPTIONS') {
|
|
@@ -187,7 +222,7 @@ export class ForkServer {
|
|
|
187
222
|
}
|
|
188
223
|
} catch (error) {
|
|
189
224
|
// Log full error server-side for diagnostics
|
|
190
|
-
|
|
225
|
+
logger.error(`Error handling request: ${error instanceof Error ? error.message : String(error)}`);
|
|
191
226
|
|
|
192
227
|
// Send generic error to client (don't expose internal details)
|
|
193
228
|
this.sendError(res, 500, 'Internal server error');
|
package/src/fork/test.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { join } from 'path';
|
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
import { runCli } from '../utils/runCli.js';
|
|
4
4
|
import type { ChildProcessAdapter } from '../utils/childProcessAdapter.js';
|
|
5
|
+
import { logger } from '../ui/index.js';
|
|
5
6
|
|
|
6
7
|
export interface SnapshotOptions {
|
|
7
8
|
path?: string;
|
|
@@ -40,7 +41,7 @@ export async function snapshot(options: SnapshotOptions = {}): Promise<string> {
|
|
|
40
41
|
const name = options.name || `snapshot-${Date.now()}`;
|
|
41
42
|
const snapshotPath = options.path || join(process.cwd(), '.movehat', 'snapshots', name);
|
|
42
43
|
|
|
43
|
-
|
|
44
|
+
logger.info(`Creating snapshot: ${name}...`);
|
|
44
45
|
|
|
45
46
|
try {
|
|
46
47
|
// Initialize fork/snapshot using aptos CLI.
|
|
@@ -68,7 +69,7 @@ export async function snapshot(options: SnapshotOptions = {}): Promise<string> {
|
|
|
68
69
|
throw new Error('Snapshot directory was not created');
|
|
69
70
|
}
|
|
70
71
|
|
|
71
|
-
|
|
72
|
+
logger.success(`Snapshot created at ${snapshotPath}`, 2);
|
|
72
73
|
return snapshotPath;
|
|
73
74
|
} catch (error) {
|
|
74
75
|
const msg = error instanceof Error ? error.message : String(error);
|
package/src/harness/Harness.ts
CHANGED
|
@@ -92,19 +92,28 @@ export class Harness {
|
|
|
92
92
|
* and `runMoveScript` throw with a message pointing at `createLocal`.
|
|
93
93
|
* `runViewFunction` works (read-only path).
|
|
94
94
|
*
|
|
95
|
-
* @param network - Network to fork
|
|
95
|
+
* @param network - Network to fork. Built-ins: `"testnet"`, `"mainnet"`.
|
|
96
|
+
* Any other name requires `rpcUrl`.
|
|
96
97
|
* @param apiKey - Optional Movement API key. When set, every upstream
|
|
97
98
|
* request from the fork's `MovementApiClient` carries
|
|
98
99
|
* `Authorization: Bearer <apiKey>`. Use for rate-limited public
|
|
99
100
|
* endpoints or auth-gated nodes. The key stays in process memory
|
|
100
101
|
* (not persisted to the fork's on-disk metadata).
|
|
102
|
+
* @param rpcUrl - Required when forking a non-built-in network.
|
|
103
|
+
* Ignored when a fork already exists on disk (the saved metadata's
|
|
104
|
+
* nodeUrl is reused).
|
|
101
105
|
*/
|
|
102
|
-
static async createFork(
|
|
106
|
+
static async createFork(
|
|
107
|
+
network: string,
|
|
108
|
+
apiKey?: string,
|
|
109
|
+
rpcUrl?: string
|
|
110
|
+
): Promise<Harness> {
|
|
103
111
|
const setupOpts: import("../types/config.js").LocalTestOptions = {
|
|
104
112
|
mode: "fork",
|
|
105
113
|
forkNetwork: network,
|
|
106
114
|
};
|
|
107
115
|
if (apiKey !== undefined) setupOpts.forkApiKey = apiKey;
|
|
116
|
+
if (rpcUrl !== undefined) setupOpts.forkRpcUrl = rpcUrl;
|
|
108
117
|
const ctx = await setupLocalTesting(setupOpts);
|
|
109
118
|
const init: HarnessInit = {
|
|
110
119
|
mode: "fork",
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { join } from "path";
|
|
3
|
-
import { randomUUID } from "crypto";
|
|
1
|
+
import { PrivateKey, PrivateKeyVariants } from "@aptos-labs/ts-sdk";
|
|
4
2
|
import type { MovehatRuntime } from "../types/runtime.js";
|
|
5
3
|
import type {
|
|
6
4
|
DeployCodeObjectOptions,
|
|
@@ -14,7 +12,7 @@ import {
|
|
|
14
12
|
validateSafeName,
|
|
15
13
|
type DeploymentInfo,
|
|
16
14
|
} from "../core/deployments.js";
|
|
17
|
-
import { validatePathSafety
|
|
15
|
+
import { validatePathSafety } from "../core/shell.js";
|
|
18
16
|
import {
|
|
19
17
|
CliExecutionError,
|
|
20
18
|
ModuleAlreadyDeployedError,
|
|
@@ -22,12 +20,11 @@ import {
|
|
|
22
20
|
} from "../errors.js";
|
|
23
21
|
import { runCli } from "../utils/runCli.js";
|
|
24
22
|
import { parseTxHash } from "../utils/parseCliOutput.js";
|
|
25
|
-
import { logger } from "../ui/index.js";
|
|
23
|
+
import { logger, isVerbose } from "../ui/index.js";
|
|
26
24
|
import {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
removeProfileSync,
|
|
25
|
+
writeTempKeyFile,
|
|
26
|
+
removeKeyFile,
|
|
27
|
+
removeKeyFileSyncBestEffort,
|
|
31
28
|
ensureSignalHandler,
|
|
32
29
|
cleanupCallbacks,
|
|
33
30
|
} from "../core/movementProfile.js";
|
|
@@ -170,9 +167,7 @@ async function executeMovementMoveObject(
|
|
|
170
167
|
}
|
|
171
168
|
|
|
172
169
|
const dir = opts.packageDir || config.moveDir;
|
|
173
|
-
const profile = `movehat-deploy-${randomUUID().slice(0, 8)}`;
|
|
174
170
|
const safeDir = validatePathSafety(dir, "package directory");
|
|
175
|
-
const safeProfile = validateProfileSafety(profile);
|
|
176
171
|
|
|
177
172
|
logger.step(
|
|
178
173
|
`${subcommand === "deploy-object" ? "Deploying" : "Upgrading"} module "${moduleName}" from ${dir}...`
|
|
@@ -211,32 +206,30 @@ async function executeMovementMoveObject(
|
|
|
211
206
|
},
|
|
212
207
|
{ adapter: opts.adapter }
|
|
213
208
|
);
|
|
214
|
-
if (buildResult.stdout)
|
|
209
|
+
if (isVerbose() && buildResult.stdout) logger.info(buildResult.stdout.trim(), 2);
|
|
215
210
|
|
|
216
|
-
//
|
|
217
|
-
// raw
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
211
|
+
// Format the private key into AIP-80 shape so the Movement CLI
|
|
212
|
+
// doesn't emit its raw-hex deprecation warning. `formatPrivateKey`
|
|
213
|
+
// is idempotent for already-prefixed inputs.
|
|
214
|
+
const formattedPrivateKey = PrivateKey.formatPrivateKey(
|
|
215
|
+
config.privateKey,
|
|
216
|
+
PrivateKeyVariants.Ed25519,
|
|
217
|
+
);
|
|
222
218
|
|
|
223
|
-
|
|
219
|
+
// Pass the private key via a 0o600 temp file (--private-key-file)
|
|
220
|
+
// and the on-chain address via --sender-account. This avoids the
|
|
221
|
+
// CLI's profile-yaml lookup entirely — no CWD / HOME / .aptos /
|
|
222
|
+
// .movement dance, no CLI-variant dependency.
|
|
223
|
+
const keyFilePath = writeTempKeyFile(formattedPrivateKey);
|
|
224
224
|
|
|
225
|
-
// Register SIGINT-safe sync cleanup BEFORE
|
|
226
|
-
//
|
|
225
|
+
// Register SIGINT-safe sync cleanup BEFORE invoking the CLI so
|
|
226
|
+
// the private key never persists on disk after an abnormal exit.
|
|
227
|
+
// The signal-handler path uses the best-effort variant because the
|
|
228
|
+
// event loop is dead and we cannot logger.warning.
|
|
227
229
|
ensureSignalHandler();
|
|
228
|
-
const syncCleanup = () =>
|
|
230
|
+
const syncCleanup = () => removeKeyFileSyncBestEffort(keyFilePath);
|
|
229
231
|
cleanupCallbacks.add(syncCleanup);
|
|
230
232
|
|
|
231
|
-
await withYamlLock(() =>
|
|
232
|
-
addProfile(movementConfigPath, profile, {
|
|
233
|
-
private_key: cleanPrivateKey,
|
|
234
|
-
public_key: account.publicKey.toString(),
|
|
235
|
-
account: deployerAddress,
|
|
236
|
-
rest_url: config.rpc,
|
|
237
|
-
})
|
|
238
|
-
);
|
|
239
|
-
|
|
240
233
|
let deployOut = "";
|
|
241
234
|
try {
|
|
242
235
|
logger.step(
|
|
@@ -258,8 +251,10 @@ async function executeMovementMoveObject(
|
|
|
258
251
|
safeDir,
|
|
259
252
|
"--url",
|
|
260
253
|
config.rpc,
|
|
261
|
-
"--
|
|
262
|
-
|
|
254
|
+
"--private-key-file",
|
|
255
|
+
keyFilePath,
|
|
256
|
+
"--sender-account",
|
|
257
|
+
deployerAddress,
|
|
263
258
|
"--assume-yes",
|
|
264
259
|
...includedArtifacts,
|
|
265
260
|
...namedAddrArgs,
|
|
@@ -270,21 +265,23 @@ async function executeMovementMoveObject(
|
|
|
270
265
|
{ adapter: opts.adapter }
|
|
271
266
|
);
|
|
272
267
|
deployOut = result.stdout;
|
|
273
|
-
|
|
274
|
-
|
|
268
|
+
// Both streams gated behind isVerbose(); see §9 — stream channel
|
|
269
|
+
// is not by itself a failure signal. Real failures throw via
|
|
270
|
+
// CliExecutionError and are surfaced from the catch below.
|
|
271
|
+
if (isVerbose() && result.stdout) logger.info(result.stdout.trim(), 2);
|
|
272
|
+
if (isVerbose() && result.stderr) logger.info(result.stderr.trim(), 2);
|
|
275
273
|
} finally {
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
//
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
);
|
|
274
|
+
// Unlink via the observable helper — emit a warning if the file
|
|
275
|
+
// could not be removed AND still exists on disk (private key
|
|
276
|
+
// would persist silently otherwise). ENOENT and races are
|
|
277
|
+
// treated as benign success.
|
|
278
|
+
const cleanupErr = removeKeyFile(keyFilePath);
|
|
279
|
+
if (cleanupErr) {
|
|
280
|
+
logger.warning(
|
|
281
|
+
`Failed to remove temp key file '${keyFilePath}': ${cleanupErr.message}. ` +
|
|
282
|
+
`The file has mode 0o600 but should be removed manually: rm ${keyFilePath}`
|
|
283
|
+
);
|
|
284
|
+
}
|
|
288
285
|
cleanupCallbacks.delete(syncCleanup);
|
|
289
286
|
}
|
|
290
287
|
|
|
@@ -342,7 +339,7 @@ async function executeMovementMoveObject(
|
|
|
342
339
|
throw error;
|
|
343
340
|
}
|
|
344
341
|
if (error instanceof CliExecutionError) {
|
|
345
|
-
if (error.stdoutPreview)
|
|
342
|
+
if (error.stdoutPreview) logger.info(error.stdoutPreview, 2);
|
|
346
343
|
logger.error(
|
|
347
344
|
`Failed to ${subcommand === "deploy-object" ? "deploy" : "upgrade"} module: ${error.message}\n${error.stderr}`
|
|
348
345
|
);
|
package/src/harness/script.ts
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { existsSync } from "fs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { randomUUID } from "crypto";
|
|
2
|
+
import { extname } from "path";
|
|
3
|
+
import { PrivateKey, PrivateKeyVariants } from "@aptos-labs/ts-sdk";
|
|
5
4
|
import type { MovehatRuntime } from "../types/runtime.js";
|
|
6
5
|
import type {
|
|
7
6
|
RunMoveScriptOptions,
|
|
8
7
|
MoveScriptResult,
|
|
9
8
|
} from "../types/harness.js";
|
|
10
|
-
import { validatePathSafety
|
|
9
|
+
import { validatePathSafety } from "../core/shell.js";
|
|
11
10
|
import { CliExecutionError } from "../errors.js";
|
|
12
11
|
import { runCli } from "../utils/runCli.js";
|
|
13
12
|
import { parseTxHash } from "../utils/parseCliOutput.js";
|
|
14
|
-
import { logger } from "../ui/index.js";
|
|
13
|
+
import { logger, isVerbose } from "../ui/index.js";
|
|
15
14
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
removeProfileSync,
|
|
15
|
+
writeTempKeyFile,
|
|
16
|
+
removeKeyFile,
|
|
17
|
+
removeKeyFileSyncBestEffort,
|
|
20
18
|
ensureSignalHandler,
|
|
21
19
|
cleanupCallbacks,
|
|
22
20
|
} from "../core/movementProfile.js";
|
|
@@ -29,9 +27,9 @@ import {
|
|
|
29
27
|
* - `.mv` compiled bytecode → `--compiled-script-path`
|
|
30
28
|
*
|
|
31
29
|
* Reuses Publisher's security model via the shared `movementProfile`
|
|
32
|
-
* helpers: per-
|
|
33
|
-
*
|
|
34
|
-
*
|
|
30
|
+
* helpers: per-invocation temp key file (0o600), SIGINT-safe sync
|
|
31
|
+
* cleanup, `--private-key-file` auth (key never appears in `ps`
|
|
32
|
+
* output or in the user's `~/.aptos/config.yaml`).
|
|
35
33
|
*
|
|
36
34
|
* Returns {@link MoveScriptResult}. `txHash` is guaranteed; `success`
|
|
37
35
|
* and `vmStatus` are best-effort parsed from the CLI's Result JSON.
|
|
@@ -70,8 +68,6 @@ export async function runMoveScript(
|
|
|
70
68
|
}
|
|
71
69
|
|
|
72
70
|
const safeScriptPath = validatePathSafety(options.scriptPath, "script path");
|
|
73
|
-
const profile = `movehat-script-${randomUUID().slice(0, 8)}`;
|
|
74
|
-
const safeProfile = validateProfileSafety(profile);
|
|
75
71
|
|
|
76
72
|
logger.step(
|
|
77
73
|
`Running Move script '${options.scriptPath}' on ${config.network}...`
|
|
@@ -80,26 +76,28 @@ export async function runMoveScript(
|
|
|
80
76
|
try {
|
|
81
77
|
const deployerAddress = account.accountAddress.toString();
|
|
82
78
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
79
|
+
// Format the private key into AIP-80 shape before writing to the
|
|
80
|
+
// temp key file. `formatPrivateKey` is idempotent for already-
|
|
81
|
+
// prefixed inputs.
|
|
82
|
+
const formattedPrivateKey = PrivateKey.formatPrivateKey(
|
|
83
|
+
config.privateKey,
|
|
84
|
+
PrivateKeyVariants.Ed25519,
|
|
85
|
+
);
|
|
87
86
|
|
|
88
|
-
|
|
87
|
+
// Pass the private key via a 0o600 temp file (--private-key-file)
|
|
88
|
+
// and the on-chain address via --sender-account. Avoids the CLI's
|
|
89
|
+
// profile-yaml lookup chain entirely (no CWD / HOME / .aptos /
|
|
90
|
+
// .movement dance, no CLI-variant dependency).
|
|
91
|
+
const keyFilePath = writeTempKeyFile(formattedPrivateKey);
|
|
89
92
|
|
|
93
|
+
// SIGINT-safe sync cleanup BEFORE the CLI call so the private key
|
|
94
|
+
// never persists on disk after an abnormal exit. The signal-handler
|
|
95
|
+
// path uses the best-effort variant because the event loop is dead
|
|
96
|
+
// and we cannot logger.warning.
|
|
90
97
|
ensureSignalHandler();
|
|
91
|
-
const syncCleanup = () =>
|
|
98
|
+
const syncCleanup = () => removeKeyFileSyncBestEffort(keyFilePath);
|
|
92
99
|
cleanupCallbacks.add(syncCleanup);
|
|
93
100
|
|
|
94
|
-
await withYamlLock(() =>
|
|
95
|
-
addProfile(movementConfigPath, profile, {
|
|
96
|
-
private_key: cleanPrivateKey,
|
|
97
|
-
public_key: account.publicKey.toString(),
|
|
98
|
-
account: deployerAddress,
|
|
99
|
-
rest_url: config.rpc,
|
|
100
|
-
})
|
|
101
|
-
);
|
|
102
|
-
|
|
103
101
|
let scriptOut = "";
|
|
104
102
|
try {
|
|
105
103
|
const typeArgsFragment: string[] =
|
|
@@ -117,8 +115,10 @@ export async function runMoveScript(
|
|
|
117
115
|
args: [
|
|
118
116
|
"move",
|
|
119
117
|
"run-script",
|
|
120
|
-
"--
|
|
121
|
-
|
|
118
|
+
"--private-key-file",
|
|
119
|
+
keyFilePath,
|
|
120
|
+
"--sender-account",
|
|
121
|
+
deployerAddress,
|
|
122
122
|
"--url",
|
|
123
123
|
config.rpc,
|
|
124
124
|
"--assume-yes",
|
|
@@ -132,18 +132,22 @@ export async function runMoveScript(
|
|
|
132
132
|
{ adapter: options.adapter }
|
|
133
133
|
);
|
|
134
134
|
scriptOut = result.stdout;
|
|
135
|
-
|
|
136
|
-
|
|
135
|
+
// Both streams gated behind isVerbose(); Movement CLI uses
|
|
136
|
+
// stderr for progress messages too. Real failures throw via
|
|
137
|
+
// CliExecutionError and are surfaced from the catch below.
|
|
138
|
+
if (isVerbose() && result.stdout) logger.info(result.stdout.trim(), 2);
|
|
139
|
+
if (isVerbose() && result.stderr) logger.info(result.stderr.trim(), 2);
|
|
137
140
|
} finally {
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
141
|
+
// Observable cleanup — emit a warning if the unlink failed and
|
|
142
|
+
// the file is still on disk (private key would persist silently
|
|
143
|
+
// otherwise).
|
|
144
|
+
const cleanupErr = removeKeyFile(keyFilePath);
|
|
145
|
+
if (cleanupErr) {
|
|
146
|
+
logger.warning(
|
|
147
|
+
`Failed to remove temp key file '${keyFilePath}': ${cleanupErr.message}. ` +
|
|
148
|
+
`The file has mode 0o600 but should be removed manually: rm ${keyFilePath}`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
147
151
|
cleanupCallbacks.delete(syncCleanup);
|
|
148
152
|
}
|
|
149
153
|
|
|
@@ -166,7 +170,7 @@ export async function runMoveScript(
|
|
|
166
170
|
return out;
|
|
167
171
|
} catch (error) {
|
|
168
172
|
if (error instanceof CliExecutionError) {
|
|
169
|
-
if (error.stdoutPreview)
|
|
173
|
+
if (error.stdoutPreview) logger.info(error.stdoutPreview, 2);
|
|
170
174
|
logger.error(`Failed to run Move script: ${error.message}\n${error.stderr}`);
|
|
171
175
|
} else {
|
|
172
176
|
const err = error instanceof Error ? error : new Error(String(error));
|