@evident-ai/cli 3.1.1-dev.7ab010b → 3.1.1-dev.7b045d5
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 +52 -20
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -11,8 +11,8 @@ import chalk2 from "chalk";
|
|
|
11
11
|
|
|
12
12
|
// src/lib/config.ts
|
|
13
13
|
import Conf from "conf";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
14
|
+
import { chmodSync, existsSync, statSync } from "fs";
|
|
15
|
+
import { dirname } from "path";
|
|
16
16
|
var PRODUCTION_API_URL = "https://api.production.evident.run/v1";
|
|
17
17
|
var PRODUCTION_TUNNEL_URL = "wss://tunnel.production.evident.run";
|
|
18
18
|
var defaults = {
|
|
@@ -47,8 +47,35 @@ var credentials = new Conf({
|
|
|
47
47
|
projectName: "evident",
|
|
48
48
|
projectSuffix: "",
|
|
49
49
|
configName: "credentials",
|
|
50
|
-
defaults: {}
|
|
50
|
+
defaults: {},
|
|
51
|
+
configFileMode: 384
|
|
51
52
|
});
|
|
53
|
+
var CREDENTIALS_FILE_MODE = 384;
|
|
54
|
+
var CREDENTIALS_DIR_MODE = 448;
|
|
55
|
+
var permissionWarningEmitted = false;
|
|
56
|
+
function hardenCredentialsPermissions() {
|
|
57
|
+
if (process.platform === "win32") {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const file = credentials.path;
|
|
61
|
+
for (const [path, mode] of [
|
|
62
|
+
[file, CREDENTIALS_FILE_MODE],
|
|
63
|
+
[dirname(file), CREDENTIALS_DIR_MODE]
|
|
64
|
+
]) {
|
|
65
|
+
try {
|
|
66
|
+
if (existsSync(path) && (statSync(path).mode & 511) !== mode) {
|
|
67
|
+
chmodSync(path, mode);
|
|
68
|
+
}
|
|
69
|
+
} catch (err) {
|
|
70
|
+
if (!permissionWarningEmitted) {
|
|
71
|
+
permissionWarningEmitted = true;
|
|
72
|
+
console.error(
|
|
73
|
+
`[config] could not restrict permissions on ${path}; the credentials file may be readable by other users on this machine: ${err instanceof Error ? err.message : String(err)}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
52
79
|
function getApiUrlConfig() {
|
|
53
80
|
return getApiUrl();
|
|
54
81
|
}
|
|
@@ -59,6 +86,7 @@ function credentialsKey() {
|
|
|
59
86
|
return getApiUrl();
|
|
60
87
|
}
|
|
61
88
|
function getCredentials() {
|
|
89
|
+
hardenCredentialsPermissions();
|
|
62
90
|
const byEndpoint = credentials.get("byEndpoint") ?? {};
|
|
63
91
|
return byEndpoint[credentialsKey()] ?? {};
|
|
64
92
|
}
|
|
@@ -70,14 +98,17 @@ function setCredentials(creds) {
|
|
|
70
98
|
expiresAt: creds.expiresAt
|
|
71
99
|
};
|
|
72
100
|
credentials.set("byEndpoint", byEndpoint);
|
|
101
|
+
hardenCredentialsPermissions();
|
|
73
102
|
}
|
|
74
103
|
function clearCredentials() {
|
|
75
104
|
const byEndpoint = credentials.get("byEndpoint") ?? {};
|
|
76
105
|
delete byEndpoint[credentialsKey()];
|
|
77
106
|
credentials.set("byEndpoint", byEndpoint);
|
|
107
|
+
hardenCredentialsPermissions();
|
|
78
108
|
}
|
|
79
109
|
function clearAllCredentials() {
|
|
80
110
|
credentials.clear();
|
|
111
|
+
hardenCredentialsPermissions();
|
|
81
112
|
}
|
|
82
113
|
function getCliName() {
|
|
83
114
|
const argv1 = process.argv[1] || "";
|
|
@@ -373,7 +404,8 @@ async function deviceFlowLogin(options) {
|
|
|
373
404
|
}
|
|
374
405
|
async function tokenLogin() {
|
|
375
406
|
console.log("Token login mode.");
|
|
376
|
-
console.log("
|
|
407
|
+
console.log("Run `evident login` on a machine with a browser to get a token.");
|
|
408
|
+
console.log("Manage or revoke existing tokens under Settings \u2192 CLI tokens.");
|
|
377
409
|
blank();
|
|
378
410
|
process.stdout.write("Paste token: ");
|
|
379
411
|
const token = await new Promise((resolve3) => {
|
|
@@ -467,8 +499,8 @@ async function whoami() {
|
|
|
467
499
|
}
|
|
468
500
|
|
|
469
501
|
// src/commands/run.ts
|
|
470
|
-
import { homedir as
|
|
471
|
-
import { isAbsolute as isAbsolute2, join as
|
|
502
|
+
import { homedir as homedir2 } from "os";
|
|
503
|
+
import { isAbsolute as isAbsolute2, join as join2, parse, resolve as resolvePath } from "path";
|
|
472
504
|
import chalk6 from "chalk";
|
|
473
505
|
|
|
474
506
|
// ../../packages/types/src/telemetry/index.ts
|
|
@@ -2042,12 +2074,12 @@ var RunnerConnection = class {
|
|
|
2042
2074
|
};
|
|
2043
2075
|
|
|
2044
2076
|
// src/lib/channels/driver.ts
|
|
2045
|
-
import { homedir
|
|
2077
|
+
import { homedir } from "os";
|
|
2046
2078
|
|
|
2047
2079
|
// src/lib/file-push.ts
|
|
2048
2080
|
import { randomUUID } from "crypto";
|
|
2049
2081
|
import { chmod, mkdir, open as open2, realpath, rename, unlink } from "fs/promises";
|
|
2050
|
-
import { basename, dirname, isAbsolute, join
|
|
2082
|
+
import { basename, dirname as dirname2, isAbsolute, join, relative, resolve as resolve2, sep } from "path";
|
|
2051
2083
|
var FILE_MODE = 384;
|
|
2052
2084
|
var DIRECTORY_MODE = 448;
|
|
2053
2085
|
async function writePushedFile(request) {
|
|
@@ -2078,9 +2110,9 @@ async function writePushedFile(request) {
|
|
|
2078
2110
|
}
|
|
2079
2111
|
try {
|
|
2080
2112
|
const { existingAncestor, missingSegments } = await resolveNearestExistingAncestor(
|
|
2081
|
-
|
|
2113
|
+
dirname2(candidate)
|
|
2082
2114
|
);
|
|
2083
|
-
const realTarget =
|
|
2115
|
+
const realTarget = join(existingAncestor, ...missingSegments, basename(candidate));
|
|
2084
2116
|
const allowedDirectory = await findContainingAllowedDirectory(allowedDirectories, realTarget);
|
|
2085
2117
|
if (allowedDirectory === null) {
|
|
2086
2118
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
@@ -2090,8 +2122,8 @@ async function writePushedFile(request) {
|
|
|
2090
2122
|
}
|
|
2091
2123
|
if (missingSegments.length > 0) {
|
|
2092
2124
|
await createMissingDirectories(existingAncestor, missingSegments);
|
|
2093
|
-
const realParent = await realpath(
|
|
2094
|
-
if (realParent !==
|
|
2125
|
+
const realParent = await realpath(dirname2(realTarget));
|
|
2126
|
+
if (realParent !== dirname2(realTarget) || !contains(allowedDirectory, realTarget)) {
|
|
2095
2127
|
return refuse("path_not_allowed", "The runner does not allow writing to that location.", {
|
|
2096
2128
|
path: realTarget,
|
|
2097
2129
|
bytes,
|
|
@@ -2116,7 +2148,7 @@ function expandAndValidate(requestedPath, homeDir) {
|
|
|
2116
2148
|
if (requestedPath.trim() === "" || requestedPath.includes("\0")) {
|
|
2117
2149
|
return null;
|
|
2118
2150
|
}
|
|
2119
|
-
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ?
|
|
2151
|
+
const expanded = requestedPath === "~" ? homeDir : requestedPath.startsWith("~/") ? join(homeDir, requestedPath.slice(2)) : requestedPath;
|
|
2120
2152
|
if (expanded.split(/[/\\]/).includes("..")) {
|
|
2121
2153
|
return null;
|
|
2122
2154
|
}
|
|
@@ -2134,7 +2166,7 @@ async function resolveNearestExistingAncestor(directory) {
|
|
|
2134
2166
|
try {
|
|
2135
2167
|
return { existingAncestor: await realpath(current), missingSegments };
|
|
2136
2168
|
} catch (err) {
|
|
2137
|
-
const parent =
|
|
2169
|
+
const parent = dirname2(current);
|
|
2138
2170
|
if (err.code !== "ENOENT" || parent === current) {
|
|
2139
2171
|
throw err;
|
|
2140
2172
|
}
|
|
@@ -2189,13 +2221,13 @@ function contains(realDirectory, realTarget) {
|
|
|
2189
2221
|
async function createMissingDirectories(existingAncestor, missingSegments) {
|
|
2190
2222
|
let current = existingAncestor;
|
|
2191
2223
|
for (const segment of missingSegments) {
|
|
2192
|
-
current =
|
|
2224
|
+
current = join(current, segment);
|
|
2193
2225
|
await mkdir(current, { recursive: true, mode: DIRECTORY_MODE });
|
|
2194
2226
|
await chmod(current, DIRECTORY_MODE);
|
|
2195
2227
|
}
|
|
2196
2228
|
}
|
|
2197
2229
|
async function writeAtomically(realTarget, content) {
|
|
2198
|
-
const temporaryPath =
|
|
2230
|
+
const temporaryPath = join(dirname2(realTarget), `.evident-push-${randomUUID()}.tmp`);
|
|
2199
2231
|
let handle;
|
|
2200
2232
|
try {
|
|
2201
2233
|
handle = await open2(temporaryPath, "wx", FILE_MODE);
|
|
@@ -2708,7 +2740,7 @@ var ChannelDriver = class _ChannelDriver {
|
|
|
2708
2740
|
this.stuckQueuedMs = config2.stuckQueuedMs ?? DEFAULT_STUCK_QUEUED_MS;
|
|
2709
2741
|
this.now = config2.now ?? (() => Date.now());
|
|
2710
2742
|
this.fileSyncDirectories = config2.fileSyncDirectories ?? [];
|
|
2711
|
-
this.homeDir = config2.homeDir ??
|
|
2743
|
+
this.homeDir = config2.homeDir ?? homedir();
|
|
2712
2744
|
}
|
|
2713
2745
|
/** The IPv4-loopback base URL for the local `opencode serve`. */
|
|
2714
2746
|
get opencodeBase() {
|
|
@@ -5106,7 +5138,7 @@ function resolveFileSyncDirectories(raw, homeDir) {
|
|
|
5106
5138
|
if (trimmed === "") {
|
|
5107
5139
|
throw new Error("--enable-file-sync-to requires a directory path (got an empty value)");
|
|
5108
5140
|
}
|
|
5109
|
-
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ?
|
|
5141
|
+
const expanded = trimmed === "~" ? homeDir : trimmed.startsWith("~/") ? join2(homeDir, trimmed.slice(2)) : trimmed;
|
|
5110
5142
|
if (!isAbsolute2(expanded)) {
|
|
5111
5143
|
throw new Error(`--enable-file-sync-to requires an absolute directory path; got "${entry}"`);
|
|
5112
5144
|
}
|
|
@@ -5447,7 +5479,7 @@ async function run(options) {
|
|
|
5447
5479
|
let fileSyncDirectories;
|
|
5448
5480
|
try {
|
|
5449
5481
|
logLevel = resolveLogLevel(options);
|
|
5450
|
-
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo,
|
|
5482
|
+
fileSyncDirectories = resolveFileSyncDirectories(options.enableFileSyncTo, homedir2());
|
|
5451
5483
|
} catch (error2) {
|
|
5452
5484
|
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
5453
5485
|
if (options.json) {
|
|
@@ -5682,7 +5714,7 @@ async function run(options) {
|
|
|
5682
5714
|
// #559: the `--enable-file-sync-to` allow-list. Empty ⇒ pending files are
|
|
5683
5715
|
// REJECTED with `file_sync_disabled` on the ack, not silently ignored.
|
|
5684
5716
|
fileSyncDirectories,
|
|
5685
|
-
homeDir:
|
|
5717
|
+
homeDir: homedir2(),
|
|
5686
5718
|
log: (entry) => (
|
|
5687
5719
|
// Thread the driver's real level straight through so `debug`/`warn`
|
|
5688
5720
|
// survive the sink filter (they no longer collapse to info). `type`
|