hereya-cli 0.103.0 → 0.105.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/README.md +68 -68
- package/dist/backend/cloud/cloud-backend/executor-jobs.d.ts +6 -0
- package/dist/backend/cloud/cloud-backend/executor-jobs.js +67 -13
- package/dist/commands/executor/start/index.js +17 -3
- package/dist/iac/terraform.js +11 -7
- package/dist/lib/executor-start/format.d.ts +9 -0
- package/dist/lib/executor-start/format.js +24 -0
- package/dist/lib/executor-start/hooks.d.ts +27 -0
- package/dist/lib/executor-start/hooks.js +42 -0
- package/dist/lib/executor-start/job-dispatch.d.ts +15 -2
- package/dist/lib/executor-start/job-dispatch.js +81 -27
- package/dist/lib/executor-start/job-serialization.d.ts +51 -0
- package/dist/lib/executor-start/job-serialization.js +134 -0
- package/dist/lib/executor-start/job-timeout.d.ts +7 -0
- package/dist/lib/executor-start/job-timeout.js +17 -0
- package/dist/lib/remote-job-utils.js +54 -18
- package/dist/lib/shell.js +4 -2
- package/oclif.manifest.json +101 -101
- package/package.json +1 -1
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-(target, workspace) job serialization for the remote executor.
|
|
3
|
+
*
|
|
4
|
+
* Production incident: a user resubmitted a deploy that looked stuck, and the
|
|
5
|
+
* executor ran BOTH identical deploys of the same project+workspace
|
|
6
|
+
* concurrently. They raced each other over the same cloned repo dirs, local
|
|
7
|
+
* hereya state and tofu/CDK state, and both wedged. Jobs that mutate the same
|
|
8
|
+
* logical target in the same workspace must run serially on one executor;
|
|
9
|
+
* unrelated jobs (e.g. the normal boot burst of provisions for different
|
|
10
|
+
* packages) must keep running concurrently.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Escape '%' (the escape character) and ':' (the key segment separator) so
|
|
14
|
+
* two different (target, workspace) pairs can never collide on the same key
|
|
15
|
+
* (e.g. project `a:b` in workspace `c` vs project `a` in workspace `b:c`).
|
|
16
|
+
*/
|
|
17
|
+
function escapeSegment(value) {
|
|
18
|
+
return value.replaceAll('%', '%25').replaceAll(':', '%3A');
|
|
19
|
+
}
|
|
20
|
+
function buildKey(kind, target, workspace) {
|
|
21
|
+
return `${kind}:${escapeSegment(target)}:${escapeSegment(workspace)}`;
|
|
22
|
+
}
|
|
23
|
+
function nonEmptyString(value) {
|
|
24
|
+
if (typeof value !== 'string')
|
|
25
|
+
return undefined;
|
|
26
|
+
const trimmed = value.trim();
|
|
27
|
+
return trimmed === '' ? undefined : trimmed;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Derives a stable key identifying "same logical target in the same
|
|
31
|
+
* workspace", or null when the job must NOT be serialized (read-only jobs,
|
|
32
|
+
* unknown job types, malformed payloads — those run immediately, no lock).
|
|
33
|
+
* Never throws.
|
|
34
|
+
*
|
|
35
|
+
* Key derivation per job type (the kind prefix prevents cross-kind
|
|
36
|
+
* collisions, e.g. a project named like an app):
|
|
37
|
+
* - deploy / undeploy: `project:<payload.project>:<payload.workspace>`
|
|
38
|
+
* - init: `project:<payload.projectName>:<payload.workspace>`
|
|
39
|
+
* - app-deploy / app-destroy: `app:<payload.appName>:<payload.workspace>`
|
|
40
|
+
* - provision / destroy: `pkg:<payload.package>:<payload.workspace>`
|
|
41
|
+
* - resolve-env: null (read-only, never serialized)
|
|
42
|
+
*/
|
|
43
|
+
export function deriveJobSerializationKey(job) {
|
|
44
|
+
try {
|
|
45
|
+
const { payload } = job;
|
|
46
|
+
if (!payload || typeof payload !== 'object')
|
|
47
|
+
return null;
|
|
48
|
+
const workspace = nonEmptyString(payload.workspace);
|
|
49
|
+
if (!workspace)
|
|
50
|
+
return null;
|
|
51
|
+
switch (job.type) {
|
|
52
|
+
case 'app-deploy':
|
|
53
|
+
case 'app-destroy': {
|
|
54
|
+
const appName = nonEmptyString(payload.appName);
|
|
55
|
+
return appName ? buildKey('app', appName, workspace) : null;
|
|
56
|
+
}
|
|
57
|
+
case 'deploy':
|
|
58
|
+
case 'undeploy': {
|
|
59
|
+
// The deploy payload carries the project NAME (the repo URL lives in
|
|
60
|
+
// the project metadata fetched later), so the name is the identity.
|
|
61
|
+
const project = nonEmptyString(payload.project);
|
|
62
|
+
return project ? buildKey('project', project, workspace) : null;
|
|
63
|
+
}
|
|
64
|
+
case 'destroy':
|
|
65
|
+
case 'provision': {
|
|
66
|
+
const pkg = nonEmptyString(payload.package);
|
|
67
|
+
return pkg ? buildKey('pkg', pkg, workspace) : null;
|
|
68
|
+
}
|
|
69
|
+
case 'init': {
|
|
70
|
+
// Shares the `project:` key space with deploy/undeploy on purpose:
|
|
71
|
+
// an init and a deploy of the same project must not interleave.
|
|
72
|
+
const projectName = nonEmptyString(payload.projectName);
|
|
73
|
+
return projectName ? buildKey('project', projectName, workspace) : null;
|
|
74
|
+
}
|
|
75
|
+
default: {
|
|
76
|
+
// resolve-env (read-only) and any unknown/future job type.
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// Serialization is best-effort protection; a weird payload must never
|
|
83
|
+
// prevent the job from running at all.
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Minimal keyed mutex: chains async functions per key so runs with the same
|
|
89
|
+
* key execute strictly serially (FIFO), while runs with different keys — or a
|
|
90
|
+
* null key — start immediately and proceed concurrently.
|
|
91
|
+
*
|
|
92
|
+
* - A rejecting fn never blocks its successors: each fn is chained via
|
|
93
|
+
* `.then(fn, fn)` onto a rejection-swallowed tail.
|
|
94
|
+
* - The map entry for a key is removed once its chain drains, so the map
|
|
95
|
+
* cannot grow unboundedly over executor uptime.
|
|
96
|
+
*/
|
|
97
|
+
export class KeyedMutex {
|
|
98
|
+
chains = new Map();
|
|
99
|
+
/** Number of keys with in-flight or queued runs (introspection/test aid). */
|
|
100
|
+
get size() {
|
|
101
|
+
return this.chains.size;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* True when at least one run for this key is in flight or queued — i.e. a
|
|
105
|
+
* new run() with this key would have to wait. A null key is never busy.
|
|
106
|
+
*/
|
|
107
|
+
isBusy(key) {
|
|
108
|
+
return key !== null && this.chains.has(key);
|
|
109
|
+
}
|
|
110
|
+
async run(key, fn) {
|
|
111
|
+
if (key === null) {
|
|
112
|
+
return fn();
|
|
113
|
+
}
|
|
114
|
+
const existing = this.chains.get(key);
|
|
115
|
+
const entry = existing ?? { pending: 0, tail: Promise.resolve() };
|
|
116
|
+
if (!existing) {
|
|
117
|
+
this.chains.set(key, entry);
|
|
118
|
+
}
|
|
119
|
+
entry.pending += 1;
|
|
120
|
+
const chained = entry.tail.then(fn, fn);
|
|
121
|
+
// The stored tail must never reject (a failed predecessor must not block
|
|
122
|
+
// or poison successors); once this run settles, drop the map entry if no
|
|
123
|
+
// other run is in flight or queued for the key.
|
|
124
|
+
entry.tail = chained
|
|
125
|
+
.then(() => { }, () => { })
|
|
126
|
+
.finally(() => {
|
|
127
|
+
entry.pending -= 1;
|
|
128
|
+
if (entry.pending === 0 && this.chains.get(key) === entry) {
|
|
129
|
+
this.chains.delete(key);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
return chained;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-job execution timeout in minutes. Overridable via
|
|
3
|
+
* HEREYA_EXECUTOR_JOB_TIMEOUT_MIN (positive number, in minutes).
|
|
4
|
+
* A wedged child process (e.g. a tool blocking on a prompt) must not hold
|
|
5
|
+
* its executor slot forever while the heartbeat keeps the job "alive".
|
|
6
|
+
*/
|
|
7
|
+
export declare function executorJobTimeoutMin(): number;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
const DEFAULT_EXECUTOR_JOB_TIMEOUT_MIN = 60;
|
|
2
|
+
/**
|
|
3
|
+
* Per-job execution timeout in minutes. Overridable via
|
|
4
|
+
* HEREYA_EXECUTOR_JOB_TIMEOUT_MIN (positive number, in minutes).
|
|
5
|
+
* A wedged child process (e.g. a tool blocking on a prompt) must not hold
|
|
6
|
+
* its executor slot forever while the heartbeat keeps the job "alive".
|
|
7
|
+
*/
|
|
8
|
+
export function executorJobTimeoutMin() {
|
|
9
|
+
const fromEnv = process.env.HEREYA_EXECUTOR_JOB_TIMEOUT_MIN;
|
|
10
|
+
if (fromEnv !== undefined) {
|
|
11
|
+
const parsed = Number(fromEnv);
|
|
12
|
+
if (Number.isFinite(parsed) && parsed > 0) {
|
|
13
|
+
return parsed;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return DEFAULT_EXECUTOR_JOB_TIMEOUT_MIN;
|
|
17
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { REQUEST_TIMED_OUT } from '../backend/cloud/cloud-backend/executor-jobs.js';
|
|
1
2
|
const DEFAULT_TIMEOUT_MIN = 90;
|
|
2
3
|
const HEARTBEAT_INTERVAL_MS = 5 * 60 * 1000;
|
|
3
4
|
/**
|
|
@@ -11,6 +12,12 @@ const REWAKE_INTERVAL_MS = 60_000;
|
|
|
11
12
|
* a chance to land before we ask for another one.
|
|
12
13
|
*/
|
|
13
14
|
const REWAKE_INITIAL_DELAY_MS = 60_000;
|
|
15
|
+
/**
|
|
16
|
+
* A status fetch aborted by its timeout (see executor-jobs.ts) is transient —
|
|
17
|
+
* retry a bounded number of consecutive times with a short backoff.
|
|
18
|
+
*/
|
|
19
|
+
const MAX_CONSECUTIVE_POLL_TIMEOUTS = 3;
|
|
20
|
+
const POLL_TIMEOUT_RETRY_DELAY_MS = 2000;
|
|
14
21
|
/**
|
|
15
22
|
* Default poll timeout, in ms. Overridable via HEREYA_JOB_TIMEOUT_MIN env var
|
|
16
23
|
* (positive integer, in minutes). Falls back to DEFAULT_TIMEOUT_MIN.
|
|
@@ -49,6 +56,33 @@ async function maybeRewake(args) {
|
|
|
49
56
|
}
|
|
50
57
|
return now;
|
|
51
58
|
}
|
|
59
|
+
/**
|
|
60
|
+
* Forwards new job logs to the logger, or emits a periodic "still waiting"
|
|
61
|
+
* heartbeat when the job has been quiet. Returns the updated cursors.
|
|
62
|
+
*/
|
|
63
|
+
function forwardJobProgress(args) {
|
|
64
|
+
const { job, jobId, logger, startTime, timeout } = args;
|
|
65
|
+
let { lastLogLength, lastUserHeartbeat } = args;
|
|
66
|
+
const sawNewLog = Boolean(job.logs && job.logs.length > lastLogLength);
|
|
67
|
+
if (sawNewLog) {
|
|
68
|
+
const newLogs = job.logs.slice(lastLogLength);
|
|
69
|
+
if (newLogs.trim()) {
|
|
70
|
+
logger?.info?.(newLogs);
|
|
71
|
+
}
|
|
72
|
+
lastLogLength = job.logs.length;
|
|
73
|
+
lastUserHeartbeat = Date.now();
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
const now = Date.now();
|
|
77
|
+
if (now - lastUserHeartbeat >= HEARTBEAT_INTERVAL_MS) {
|
|
78
|
+
const elapsedMin = Math.round((now - startTime) / 60_000);
|
|
79
|
+
const totalMin = Math.round(timeout / 60_000);
|
|
80
|
+
logger?.info?.(`still waiting on job ${jobId} (${elapsedMin}m elapsed, ${totalMin}m timeout). Set HEREYA_JOB_TIMEOUT_MIN to extend.`);
|
|
81
|
+
lastUserHeartbeat = now;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return { lastLogLength, lastUserHeartbeat };
|
|
85
|
+
}
|
|
52
86
|
export async function pollExecutorJob(input) {
|
|
53
87
|
const { cloudBackend, jobId, logger, workspace } = input;
|
|
54
88
|
const timeout = input.timeoutMs ?? defaultTimeoutMs();
|
|
@@ -58,6 +92,7 @@ export async function pollExecutorJob(input) {
|
|
|
58
92
|
let lastStatus = 'pending';
|
|
59
93
|
let lastUserHeartbeat = startTime;
|
|
60
94
|
let lastRewakeAt = startTime;
|
|
95
|
+
let consecutiveTimeouts = 0;
|
|
61
96
|
while (Date.now() - startTime < timeout) {
|
|
62
97
|
// eslint-disable-next-line no-await-in-loop
|
|
63
98
|
const statusResult = await cloudBackend.getExecutorJobStatus({
|
|
@@ -67,28 +102,29 @@ export async function pollExecutorJob(input) {
|
|
|
67
102
|
workspace,
|
|
68
103
|
});
|
|
69
104
|
if (!statusResult.success) {
|
|
105
|
+
if (statusResult.reason === REQUEST_TIMED_OUT && consecutiveTimeouts < MAX_CONSECUTIVE_POLL_TIMEOUTS) {
|
|
106
|
+
consecutiveTimeouts += 1;
|
|
107
|
+
logger?.debug?.(`Job status poll timed out (attempt ${consecutiveTimeouts}/${MAX_CONSECUTIVE_POLL_TIMEOUTS}); retrying...`);
|
|
108
|
+
// eslint-disable-next-line no-await-in-loop
|
|
109
|
+
await new Promise((resolve) => {
|
|
110
|
+
setTimeout(resolve, POLL_TIMEOUT_RETRY_DELAY_MS);
|
|
111
|
+
});
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
70
114
|
return { reason: `Failed to get job status: ${statusResult.reason}`, success: false };
|
|
71
115
|
}
|
|
116
|
+
consecutiveTimeouts = 0;
|
|
72
117
|
const { job } = statusResult;
|
|
73
118
|
lastStatus = job.status;
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
const now = Date.now();
|
|
85
|
-
if (now - lastUserHeartbeat >= HEARTBEAT_INTERVAL_MS) {
|
|
86
|
-
const elapsedMin = Math.round((now - startTime) / 60_000);
|
|
87
|
-
const totalMin = Math.round(timeout / 60_000);
|
|
88
|
-
logger?.info?.(`still waiting on job ${jobId} (${elapsedMin}m elapsed, ${totalMin}m timeout). Set HEREYA_JOB_TIMEOUT_MIN to extend.`);
|
|
89
|
-
lastUserHeartbeat = now;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
119
|
+
({ lastLogLength, lastUserHeartbeat } = forwardJobProgress({
|
|
120
|
+
job,
|
|
121
|
+
jobId,
|
|
122
|
+
lastLogLength,
|
|
123
|
+
lastUserHeartbeat,
|
|
124
|
+
logger,
|
|
125
|
+
startTime,
|
|
126
|
+
timeout,
|
|
127
|
+
}));
|
|
92
128
|
// eslint-disable-next-line no-await-in-loop
|
|
93
129
|
lastRewakeAt = await maybeRewake({
|
|
94
130
|
cloudBackend,
|
package/dist/lib/shell.js
CHANGED
|
@@ -34,8 +34,10 @@ export async function runShell(cmd, args, options = {}) {
|
|
|
34
34
|
cwd: options.directory ?? process.cwd(),
|
|
35
35
|
env: { ...process.env, ...options.env },
|
|
36
36
|
shell: true,
|
|
37
|
-
//
|
|
38
|
-
|
|
37
|
+
// Default: stdin ignored (closed), stdout/stderr piped. An open stdin
|
|
38
|
+
// pipe nothing writes to lets prompting tools (tofu, git, npm) block
|
|
39
|
+
// forever; with 'ignore' they read EOF and fail fast instead.
|
|
40
|
+
stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'],
|
|
39
41
|
};
|
|
40
42
|
logger.debug(`Executing: ${cmd} ${args.join(' ')}\n`);
|
|
41
43
|
const child = spawn(cmd, args, spawnOptions);
|
package/oclif.manifest.json
CHANGED
|
@@ -2402,6 +2402,61 @@
|
|
|
2402
2402
|
"index.js"
|
|
2403
2403
|
]
|
|
2404
2404
|
},
|
|
2405
|
+
"flow:remove": {
|
|
2406
|
+
"aliases": [],
|
|
2407
|
+
"args": {
|
|
2408
|
+
"package": {
|
|
2409
|
+
"description": "Remove a previously added package.",
|
|
2410
|
+
"name": "package",
|
|
2411
|
+
"required": true
|
|
2412
|
+
}
|
|
2413
|
+
},
|
|
2414
|
+
"description": "Remove a package from the project in a git branch-based workspace",
|
|
2415
|
+
"examples": [
|
|
2416
|
+
"<%= config.bin %> <%= command.id %> cloudy/docker_postgres",
|
|
2417
|
+
"<%= config.bin %> <%= command.id %> cloudy/docker_postgres --profile staging"
|
|
2418
|
+
],
|
|
2419
|
+
"flags": {
|
|
2420
|
+
"chdir": {
|
|
2421
|
+
"description": "\n Directory where the command will be executed.\n If not specified, it defaults to the current working directory.\n Alternatively, you can define the project root by setting the HEREYA_PROJECT_ROOT_DIR environment variable.\n ",
|
|
2422
|
+
"name": "chdir",
|
|
2423
|
+
"required": false,
|
|
2424
|
+
"hasDynamicHelp": false,
|
|
2425
|
+
"multiple": false,
|
|
2426
|
+
"type": "option"
|
|
2427
|
+
},
|
|
2428
|
+
"debug": {
|
|
2429
|
+
"description": "enable debug mode",
|
|
2430
|
+
"name": "debug",
|
|
2431
|
+
"allowNo": false,
|
|
2432
|
+
"type": "boolean"
|
|
2433
|
+
},
|
|
2434
|
+
"profile": {
|
|
2435
|
+
"description": "profile to use for the workspace (will be appended to workspace name)",
|
|
2436
|
+
"name": "profile",
|
|
2437
|
+
"required": false,
|
|
2438
|
+
"hasDynamicHelp": false,
|
|
2439
|
+
"multiple": false,
|
|
2440
|
+
"type": "option"
|
|
2441
|
+
}
|
|
2442
|
+
},
|
|
2443
|
+
"hasDynamicHelp": false,
|
|
2444
|
+
"hiddenAliases": [],
|
|
2445
|
+
"id": "flow:remove",
|
|
2446
|
+
"pluginAlias": "hereya-cli",
|
|
2447
|
+
"pluginName": "hereya-cli",
|
|
2448
|
+
"pluginType": "core",
|
|
2449
|
+
"strict": true,
|
|
2450
|
+
"enableJsonFlag": false,
|
|
2451
|
+
"isESM": true,
|
|
2452
|
+
"relativePath": [
|
|
2453
|
+
"dist",
|
|
2454
|
+
"commands",
|
|
2455
|
+
"flow",
|
|
2456
|
+
"remove",
|
|
2457
|
+
"index.js"
|
|
2458
|
+
]
|
|
2459
|
+
},
|
|
2405
2460
|
"flow:run": {
|
|
2406
2461
|
"aliases": [],
|
|
2407
2462
|
"args": {
|
|
@@ -2992,61 +3047,6 @@
|
|
|
2992
3047
|
"index.js"
|
|
2993
3048
|
]
|
|
2994
3049
|
},
|
|
2995
|
-
"flow:remove": {
|
|
2996
|
-
"aliases": [],
|
|
2997
|
-
"args": {
|
|
2998
|
-
"package": {
|
|
2999
|
-
"description": "Remove a previously added package.",
|
|
3000
|
-
"name": "package",
|
|
3001
|
-
"required": true
|
|
3002
|
-
}
|
|
3003
|
-
},
|
|
3004
|
-
"description": "Remove a package from the project in a git branch-based workspace",
|
|
3005
|
-
"examples": [
|
|
3006
|
-
"<%= config.bin %> <%= command.id %> cloudy/docker_postgres",
|
|
3007
|
-
"<%= config.bin %> <%= command.id %> cloudy/docker_postgres --profile staging"
|
|
3008
|
-
],
|
|
3009
|
-
"flags": {
|
|
3010
|
-
"chdir": {
|
|
3011
|
-
"description": "\n Directory where the command will be executed.\n If not specified, it defaults to the current working directory.\n Alternatively, you can define the project root by setting the HEREYA_PROJECT_ROOT_DIR environment variable.\n ",
|
|
3012
|
-
"name": "chdir",
|
|
3013
|
-
"required": false,
|
|
3014
|
-
"hasDynamicHelp": false,
|
|
3015
|
-
"multiple": false,
|
|
3016
|
-
"type": "option"
|
|
3017
|
-
},
|
|
3018
|
-
"debug": {
|
|
3019
|
-
"description": "enable debug mode",
|
|
3020
|
-
"name": "debug",
|
|
3021
|
-
"allowNo": false,
|
|
3022
|
-
"type": "boolean"
|
|
3023
|
-
},
|
|
3024
|
-
"profile": {
|
|
3025
|
-
"description": "profile to use for the workspace (will be appended to workspace name)",
|
|
3026
|
-
"name": "profile",
|
|
3027
|
-
"required": false,
|
|
3028
|
-
"hasDynamicHelp": false,
|
|
3029
|
-
"multiple": false,
|
|
3030
|
-
"type": "option"
|
|
3031
|
-
}
|
|
3032
|
-
},
|
|
3033
|
-
"hasDynamicHelp": false,
|
|
3034
|
-
"hiddenAliases": [],
|
|
3035
|
-
"id": "flow:remove",
|
|
3036
|
-
"pluginAlias": "hereya-cli",
|
|
3037
|
-
"pluginName": "hereya-cli",
|
|
3038
|
-
"pluginType": "core",
|
|
3039
|
-
"strict": true,
|
|
3040
|
-
"enableJsonFlag": false,
|
|
3041
|
-
"isESM": true,
|
|
3042
|
-
"relativePath": [
|
|
3043
|
-
"dist",
|
|
3044
|
-
"commands",
|
|
3045
|
-
"flow",
|
|
3046
|
-
"remove",
|
|
3047
|
-
"index.js"
|
|
3048
|
-
]
|
|
3049
|
-
},
|
|
3050
3050
|
"devenv:project:init": {
|
|
3051
3051
|
"aliases": [],
|
|
3052
3052
|
"args": {
|
|
@@ -3314,6 +3314,51 @@
|
|
|
3314
3314
|
"index.js"
|
|
3315
3315
|
]
|
|
3316
3316
|
},
|
|
3317
|
+
"workspace:env:unset": {
|
|
3318
|
+
"aliases": [],
|
|
3319
|
+
"args": {},
|
|
3320
|
+
"description": "unset an env var for a workspace",
|
|
3321
|
+
"examples": [
|
|
3322
|
+
"<%= config.bin %> <%= command.id %> -w my-workspace -n myVar"
|
|
3323
|
+
],
|
|
3324
|
+
"flags": {
|
|
3325
|
+
"name": {
|
|
3326
|
+
"char": "n",
|
|
3327
|
+
"description": "name of the env var to unset",
|
|
3328
|
+
"name": "name",
|
|
3329
|
+
"required": true,
|
|
3330
|
+
"hasDynamicHelp": false,
|
|
3331
|
+
"multiple": false,
|
|
3332
|
+
"type": "option"
|
|
3333
|
+
},
|
|
3334
|
+
"workspace": {
|
|
3335
|
+
"char": "w",
|
|
3336
|
+
"description": "name of the workspace to unset an env var for",
|
|
3337
|
+
"name": "workspace",
|
|
3338
|
+
"required": true,
|
|
3339
|
+
"hasDynamicHelp": false,
|
|
3340
|
+
"multiple": false,
|
|
3341
|
+
"type": "option"
|
|
3342
|
+
}
|
|
3343
|
+
},
|
|
3344
|
+
"hasDynamicHelp": false,
|
|
3345
|
+
"hiddenAliases": [],
|
|
3346
|
+
"id": "workspace:env:unset",
|
|
3347
|
+
"pluginAlias": "hereya-cli",
|
|
3348
|
+
"pluginName": "hereya-cli",
|
|
3349
|
+
"pluginType": "core",
|
|
3350
|
+
"strict": true,
|
|
3351
|
+
"enableJsonFlag": false,
|
|
3352
|
+
"isESM": true,
|
|
3353
|
+
"relativePath": [
|
|
3354
|
+
"dist",
|
|
3355
|
+
"commands",
|
|
3356
|
+
"workspace",
|
|
3357
|
+
"env",
|
|
3358
|
+
"unset",
|
|
3359
|
+
"index.js"
|
|
3360
|
+
]
|
|
3361
|
+
},
|
|
3317
3362
|
"workspace:executor:install": {
|
|
3318
3363
|
"aliases": [],
|
|
3319
3364
|
"args": {},
|
|
@@ -3470,52 +3515,7 @@
|
|
|
3470
3515
|
"uninstall",
|
|
3471
3516
|
"index.js"
|
|
3472
3517
|
]
|
|
3473
|
-
},
|
|
3474
|
-
"workspace:env:unset": {
|
|
3475
|
-
"aliases": [],
|
|
3476
|
-
"args": {},
|
|
3477
|
-
"description": "unset an env var for a workspace",
|
|
3478
|
-
"examples": [
|
|
3479
|
-
"<%= config.bin %> <%= command.id %> -w my-workspace -n myVar"
|
|
3480
|
-
],
|
|
3481
|
-
"flags": {
|
|
3482
|
-
"name": {
|
|
3483
|
-
"char": "n",
|
|
3484
|
-
"description": "name of the env var to unset",
|
|
3485
|
-
"name": "name",
|
|
3486
|
-
"required": true,
|
|
3487
|
-
"hasDynamicHelp": false,
|
|
3488
|
-
"multiple": false,
|
|
3489
|
-
"type": "option"
|
|
3490
|
-
},
|
|
3491
|
-
"workspace": {
|
|
3492
|
-
"char": "w",
|
|
3493
|
-
"description": "name of the workspace to unset an env var for",
|
|
3494
|
-
"name": "workspace",
|
|
3495
|
-
"required": true,
|
|
3496
|
-
"hasDynamicHelp": false,
|
|
3497
|
-
"multiple": false,
|
|
3498
|
-
"type": "option"
|
|
3499
|
-
}
|
|
3500
|
-
},
|
|
3501
|
-
"hasDynamicHelp": false,
|
|
3502
|
-
"hiddenAliases": [],
|
|
3503
|
-
"id": "workspace:env:unset",
|
|
3504
|
-
"pluginAlias": "hereya-cli",
|
|
3505
|
-
"pluginName": "hereya-cli",
|
|
3506
|
-
"pluginType": "core",
|
|
3507
|
-
"strict": true,
|
|
3508
|
-
"enableJsonFlag": false,
|
|
3509
|
-
"isESM": true,
|
|
3510
|
-
"relativePath": [
|
|
3511
|
-
"dist",
|
|
3512
|
-
"commands",
|
|
3513
|
-
"workspace",
|
|
3514
|
-
"env",
|
|
3515
|
-
"unset",
|
|
3516
|
-
"index.js"
|
|
3517
|
-
]
|
|
3518
3518
|
}
|
|
3519
3519
|
},
|
|
3520
|
-
"version": "0.
|
|
3520
|
+
"version": "0.105.0"
|
|
3521
3521
|
}
|