@arcote.tech/arc-cli 0.6.0 → 0.6.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/index.js +31 -16
- package/package.json +7 -7
- package/runtime/entrypoint.sh +31 -0
- package/src/commands/platform-deploy.ts +21 -5
- package/src/deploy/ssh.ts +14 -4
- package/src/platform/shared.ts +4 -1
package/dist/index.js
CHANGED
|
@@ -27452,7 +27452,7 @@ function resolveWorkspace() {
|
|
|
27452
27452
|
log2("Scanning workspaces...");
|
|
27453
27453
|
const packages = discoverPackages(rootDir);
|
|
27454
27454
|
ok(`Found ${packages.length} package(s): ${packages.map((p) => p.name).join(", ")}`);
|
|
27455
|
-
if (packages.length === 0) {
|
|
27455
|
+
if (packages.length === 0 && process.env.ARC_DEPLOY_API !== "1") {
|
|
27456
27456
|
err("No workspace packages found.");
|
|
27457
27457
|
process.exit(1);
|
|
27458
27458
|
}
|
|
@@ -27821,7 +27821,7 @@ async function platformBuild(opts = {}) {
|
|
|
27821
27821
|
|
|
27822
27822
|
// src/commands/platform-deploy.ts
|
|
27823
27823
|
import { existsSync as existsSync14, readFileSync as readFileSync14 } from "fs";
|
|
27824
|
-
import { join as join18 } from "path";
|
|
27824
|
+
import { dirname as dirname7, join as join18 } from "path";
|
|
27825
27825
|
|
|
27826
27826
|
// src/deploy/bootstrap.ts
|
|
27827
27827
|
import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync14 } from "fs";
|
|
@@ -28510,17 +28510,19 @@ async function streamToString(stream2) {
|
|
|
28510
28510
|
return new Response(stream2).text();
|
|
28511
28511
|
}
|
|
28512
28512
|
function baseSshArgs(target) {
|
|
28513
|
-
const
|
|
28513
|
+
const key = target.sshKey ?? `${process.env.HOME}/.ssh/id_ed25519`;
|
|
28514
|
+
return [
|
|
28514
28515
|
"-o",
|
|
28515
28516
|
"BatchMode=yes",
|
|
28516
28517
|
"-o",
|
|
28517
28518
|
"StrictHostKeyChecking=accept-new",
|
|
28519
|
+
"-o",
|
|
28520
|
+
"IdentitiesOnly=yes",
|
|
28521
|
+
"-i",
|
|
28522
|
+
key,
|
|
28518
28523
|
"-p",
|
|
28519
28524
|
String(target.port)
|
|
28520
28525
|
];
|
|
28521
|
-
if (target.sshKey)
|
|
28522
|
-
args.push("-i", target.sshKey);
|
|
28523
|
-
return args;
|
|
28524
28526
|
}
|
|
28525
28527
|
async function sshExec(target, cmd, opts = {}) {
|
|
28526
28528
|
const args = [
|
|
@@ -28574,16 +28576,19 @@ async function waitForSsh(target, opts = {}) {
|
|
|
28574
28576
|
throw new Error(`Timed out waiting for SSH on ${target.user}@${target.host}`);
|
|
28575
28577
|
}
|
|
28576
28578
|
async function scpUpload(target, localPath, remotePath) {
|
|
28579
|
+
const key = target.sshKey ?? `${process.env.HOME}/.ssh/id_ed25519`;
|
|
28577
28580
|
const args = [
|
|
28578
28581
|
"-o",
|
|
28579
28582
|
"BatchMode=yes",
|
|
28580
28583
|
"-o",
|
|
28581
28584
|
"StrictHostKeyChecking=accept-new",
|
|
28585
|
+
"-o",
|
|
28586
|
+
"IdentitiesOnly=yes",
|
|
28587
|
+
"-i",
|
|
28588
|
+
key,
|
|
28582
28589
|
"-P",
|
|
28583
28590
|
String(target.port)
|
|
28584
28591
|
];
|
|
28585
|
-
if (target.sshKey)
|
|
28586
|
-
args.push("-i", target.sshKey);
|
|
28587
28592
|
args.push(localPath, `${target.user}@${target.host}:${remotePath}`);
|
|
28588
28593
|
const proc2 = spawn3({ cmd: ["scp", ...args], stderr: "pipe" });
|
|
28589
28594
|
const [stderr, exitCode] = await Promise.all([
|
|
@@ -29666,13 +29671,23 @@ async function platformDeploy(envArg, options = {}) {
|
|
|
29666
29671
|
}
|
|
29667
29672
|
}
|
|
29668
29673
|
function readCliVersion() {
|
|
29674
|
+
const candidates = [];
|
|
29675
|
+
const entry = process.argv[1];
|
|
29676
|
+
if (entry) {
|
|
29677
|
+
candidates.push(join18(dirname7(entry), "..", "package.json"));
|
|
29678
|
+
}
|
|
29669
29679
|
try {
|
|
29670
|
-
|
|
29671
|
-
|
|
29672
|
-
|
|
29673
|
-
|
|
29674
|
-
|
|
29680
|
+
candidates.push(join18(import.meta.dir, "..", "..", "package.json"));
|
|
29681
|
+
} catch {}
|
|
29682
|
+
for (const path4 of candidates) {
|
|
29683
|
+
try {
|
|
29684
|
+
const pkg = JSON.parse(readFileSync14(path4, "utf-8"));
|
|
29685
|
+
if (pkg.name === "@arcote.tech/arc-cli" && pkg.version) {
|
|
29686
|
+
return pkg.version;
|
|
29687
|
+
}
|
|
29688
|
+
} catch {}
|
|
29675
29689
|
}
|
|
29690
|
+
return "unknown";
|
|
29676
29691
|
}
|
|
29677
29692
|
async function hashDeployConfig(rootDir) {
|
|
29678
29693
|
const p2 = join18(rootDir, "deploy.arc.json");
|
|
@@ -31147,7 +31162,7 @@ import {
|
|
|
31147
31162
|
rmSync as rmSync4,
|
|
31148
31163
|
writeFileSync as writeFileSync15
|
|
31149
31164
|
} from "fs";
|
|
31150
|
-
import { dirname as
|
|
31165
|
+
import { dirname as dirname8, join as join19, normalize as normalize2, resolve } from "path";
|
|
31151
31166
|
function createDeployApiHandler(opts) {
|
|
31152
31167
|
return async (req, url, ctx) => {
|
|
31153
31168
|
const p3 = url.pathname;
|
|
@@ -31290,7 +31305,7 @@ function createDeployApiHandler(opts) {
|
|
|
31290
31305
|
if (existsSync15(dst)) {
|
|
31291
31306
|
rmSync4(dst, { recursive: true, force: true });
|
|
31292
31307
|
}
|
|
31293
|
-
mkdirSync14(
|
|
31308
|
+
mkdirSync14(dirname8(dst), { recursive: true });
|
|
31294
31309
|
cpSync(src2, dst, { recursive: true });
|
|
31295
31310
|
}
|
|
31296
31311
|
}
|
|
@@ -31328,7 +31343,7 @@ async function writeField(targetDir, name, file) {
|
|
|
31328
31343
|
if (!full.startsWith(safeRoot + "/") && full !== safeRoot) {
|
|
31329
31344
|
throw new Error(`Path traversal rejected: ${name}`);
|
|
31330
31345
|
}
|
|
31331
|
-
mkdirSync14(
|
|
31346
|
+
mkdirSync14(dirname8(full), { recursive: true });
|
|
31332
31347
|
writeFileSync15(full, Buffer.from(await file.arrayBuffer()));
|
|
31333
31348
|
}
|
|
31334
31349
|
function sanitizeName2(name) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@arcote.tech/arc-cli",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.1",
|
|
4
4
|
"description": "CLI tool for Arc framework",
|
|
5
5
|
"module": "index.ts",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -12,12 +12,12 @@
|
|
|
12
12
|
"build": "bun build --target=bun ./src/index.ts --outdir=dist --external @arcote.tech/arc --external @arcote.tech/arc-ds --external @arcote.tech/arc-react --external @arcote.tech/platform && chmod +x dist/index.js"
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
|
-
"@arcote.tech/arc": "^0.6.
|
|
16
|
-
"@arcote.tech/arc-ds": "^0.6.
|
|
17
|
-
"@arcote.tech/arc-react": "^0.6.
|
|
18
|
-
"@arcote.tech/arc-host": "^0.6.
|
|
19
|
-
"@arcote.tech/arc-adapter-db-sqlite": "^0.6.
|
|
20
|
-
"@arcote.tech/platform": "^0.6.
|
|
15
|
+
"@arcote.tech/arc": "^0.6.1",
|
|
16
|
+
"@arcote.tech/arc-ds": "^0.6.1",
|
|
17
|
+
"@arcote.tech/arc-react": "^0.6.1",
|
|
18
|
+
"@arcote.tech/arc-host": "^0.6.1",
|
|
19
|
+
"@arcote.tech/arc-adapter-db-sqlite": "^0.6.1",
|
|
20
|
+
"@arcote.tech/platform": "^0.6.1",
|
|
21
21
|
"@clack/prompts": "^0.9.0",
|
|
22
22
|
"commander": "^11.1.0",
|
|
23
23
|
"chokidar": "^3.5.3",
|
package/runtime/entrypoint.sh
CHANGED
|
@@ -23,5 +23,36 @@ if [ ! -f "$CLI_BIN" ]; then
|
|
|
23
23
|
bun add "@arcote.tech/arc-cli@${ARC_CLI_VERSION}"
|
|
24
24
|
fi
|
|
25
25
|
|
|
26
|
+
# resolveWorkspace() in arc platform start exits hard if /app has no
|
|
27
|
+
# package.json. In runtime mode the workspace lives in /app/.arc/platform/
|
|
28
|
+
# but the CLI still walks up from cwd. Drop a stub manifest here so the
|
|
29
|
+
# walk-up resolves to /app/.arc/platform (or to a stable "no workspace"
|
|
30
|
+
# state in pre-deploy).
|
|
31
|
+
if [ ! -f /app/package.json ]; then
|
|
32
|
+
cat > /app/package.json <<'EOF'
|
|
33
|
+
{
|
|
34
|
+
"name": "arc-runtime",
|
|
35
|
+
"private": true,
|
|
36
|
+
"type": "module",
|
|
37
|
+
"workspaces": []
|
|
38
|
+
}
|
|
39
|
+
EOF
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# Make /app/.arc/platform the working directory — that's where deployed user
|
|
43
|
+
# code, deps and node_modules live (volume mount).
|
|
44
|
+
mkdir -p /app/.arc/platform
|
|
45
|
+
cd /app/.arc/platform
|
|
46
|
+
if [ ! -f package.json ]; then
|
|
47
|
+
cat > package.json <<'EOF'
|
|
48
|
+
{
|
|
49
|
+
"name": "arc-platform-runtime",
|
|
50
|
+
"private": true,
|
|
51
|
+
"type": "module",
|
|
52
|
+
"workspaces": []
|
|
53
|
+
}
|
|
54
|
+
EOF
|
|
55
|
+
fi
|
|
56
|
+
|
|
26
57
|
echo "[entrypoint] starting arc platform (cli=${ARC_CLI_VERSION})"
|
|
27
58
|
exec bun run "$CLI_BIN" platform start
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from "fs";
|
|
2
|
-
import { join } from "path";
|
|
2
|
+
import { dirname, join } from "path";
|
|
3
3
|
import { bootstrap } from "../deploy/bootstrap";
|
|
4
4
|
import {
|
|
5
5
|
deployConfigExists,
|
|
@@ -125,13 +125,29 @@ export async function platformDeploy(
|
|
|
125
125
|
// ---------------------------------------------------------------------------
|
|
126
126
|
|
|
127
127
|
function readCliVersion(): string {
|
|
128
|
+
// import.meta.dir gets mangled by `bun build` — derive from process.argv[1]
|
|
129
|
+
// (the bundled dist/index.js path) which is stable across run modes.
|
|
130
|
+
const candidates: string[] = [];
|
|
131
|
+
const entry = process.argv[1];
|
|
132
|
+
if (entry) {
|
|
133
|
+
candidates.push(join(dirname(entry), "..", "package.json"));
|
|
134
|
+
}
|
|
128
135
|
try {
|
|
129
|
-
|
|
130
|
-
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
131
|
-
return pkg.version ?? "unknown";
|
|
136
|
+
candidates.push(join(import.meta.dir, "..", "..", "package.json"));
|
|
132
137
|
} catch {
|
|
133
|
-
|
|
138
|
+
// import.meta.dir unavailable
|
|
139
|
+
}
|
|
140
|
+
for (const path of candidates) {
|
|
141
|
+
try {
|
|
142
|
+
const pkg = JSON.parse(readFileSync(path, "utf-8"));
|
|
143
|
+
if (pkg.name === "@arcote.tech/arc-cli" && pkg.version) {
|
|
144
|
+
return pkg.version as string;
|
|
145
|
+
}
|
|
146
|
+
} catch {
|
|
147
|
+
// Try next
|
|
148
|
+
}
|
|
134
149
|
}
|
|
150
|
+
return "unknown";
|
|
135
151
|
}
|
|
136
152
|
|
|
137
153
|
async function hashDeployConfig(rootDir: string): Promise<string> {
|
package/src/deploy/ssh.ts
CHANGED
|
@@ -21,16 +21,22 @@ export interface SshExecResult {
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
function baseSshArgs(target: DeployTarget): string[] {
|
|
24
|
-
|
|
24
|
+
// IdentitiesOnly=yes pins auth to the explicit key. Without it ssh-agent
|
|
25
|
+
// offers every loaded identity and trips MaxAuthTries on hardened sshd
|
|
26
|
+
// (ansible's sshd hardening + fail2ban lowers the threshold).
|
|
27
|
+
const key = target.sshKey ?? `${process.env.HOME}/.ssh/id_ed25519`;
|
|
28
|
+
return [
|
|
25
29
|
"-o",
|
|
26
30
|
"BatchMode=yes",
|
|
27
31
|
"-o",
|
|
28
32
|
"StrictHostKeyChecking=accept-new",
|
|
33
|
+
"-o",
|
|
34
|
+
"IdentitiesOnly=yes",
|
|
35
|
+
"-i",
|
|
36
|
+
key,
|
|
29
37
|
"-p",
|
|
30
38
|
String(target.port),
|
|
31
39
|
];
|
|
32
|
-
if (target.sshKey) args.push("-i", target.sshKey);
|
|
33
|
-
return args;
|
|
34
40
|
}
|
|
35
41
|
|
|
36
42
|
/**
|
|
@@ -115,15 +121,19 @@ export async function scpUpload(
|
|
|
115
121
|
localPath: string,
|
|
116
122
|
remotePath: string,
|
|
117
123
|
): Promise<void> {
|
|
124
|
+
const key = target.sshKey ?? `${process.env.HOME}/.ssh/id_ed25519`;
|
|
118
125
|
const args = [
|
|
119
126
|
"-o",
|
|
120
127
|
"BatchMode=yes",
|
|
121
128
|
"-o",
|
|
122
129
|
"StrictHostKeyChecking=accept-new",
|
|
130
|
+
"-o",
|
|
131
|
+
"IdentitiesOnly=yes",
|
|
132
|
+
"-i",
|
|
133
|
+
key,
|
|
123
134
|
"-P",
|
|
124
135
|
String(target.port),
|
|
125
136
|
];
|
|
126
|
-
if (target.sshKey) args.push("-i", target.sshKey);
|
|
127
137
|
args.push(localPath, `${target.user}@${target.host}:${remotePath}`);
|
|
128
138
|
|
|
129
139
|
const proc = spawn({ cmd: ["scp", ...args], stderr: "pipe" });
|
package/src/platform/shared.ts
CHANGED
|
@@ -97,7 +97,10 @@ export function resolveWorkspace(): WorkspaceInfo {
|
|
|
97
97
|
`Found ${packages.length} package(s): ${packages.map((p) => p.name).join(", ")}`,
|
|
98
98
|
);
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
// Empty package list is allowed in pre-deploy runtime mode (container with
|
|
101
|
+
// freshly-mounted volume, no user code pushed yet). The CLI entry that
|
|
102
|
+
// forbids this (arc platform build/dev) checks explicitly.
|
|
103
|
+
if (packages.length === 0 && process.env.ARC_DEPLOY_API !== "1") {
|
|
101
104
|
err("No workspace packages found.");
|
|
102
105
|
process.exit(1);
|
|
103
106
|
}
|