happy-stacks 0.2.0 → 0.4.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 +84 -25
- package/bin/happys.mjs +116 -17
- package/docs/happy-development.md +2 -2
- package/docs/isolated-linux-vm.md +82 -0
- package/docs/mobile-ios.md +112 -54
- package/package.json +5 -1
- package/scripts/auth.mjs +59 -208
- package/scripts/build.mjs +58 -12
- package/scripts/cli-link.mjs +3 -3
- package/scripts/completion.mjs +5 -5
- package/scripts/daemon.mjs +168 -20
- package/scripts/dev.mjs +196 -70
- package/scripts/doctor.mjs +20 -36
- package/scripts/edison.mjs +105 -78
- package/scripts/happy.mjs +8 -19
- package/scripts/init.mjs +8 -14
- package/scripts/install.mjs +119 -23
- package/scripts/lint.mjs +31 -32
- package/scripts/menubar.mjs +6 -13
- package/scripts/migrate.mjs +11 -21
- package/scripts/mobile.mjs +93 -108
- package/scripts/mobile_dev_client.mjs +83 -0
- package/scripts/provision/linux-ubuntu-review-pr.sh +51 -0
- package/scripts/review.mjs +217 -0
- package/scripts/review_pr.mjs +368 -0
- package/scripts/run.mjs +95 -21
- package/scripts/self.mjs +11 -29
- package/scripts/server_flavor.mjs +4 -4
- package/scripts/service.mjs +19 -29
- package/scripts/setup.mjs +63 -160
- package/scripts/setup_pr.mjs +592 -52
- package/scripts/stack.mjs +608 -200
- package/scripts/stop.mjs +3 -3
- package/scripts/tailscale.mjs +44 -11
- package/scripts/test.mjs +52 -36
- package/scripts/tui.mjs +314 -74
- package/scripts/typecheck.mjs +31 -32
- package/scripts/ui_gateway.mjs +1 -1
- package/scripts/uninstall.mjs +6 -6
- package/scripts/utils/auth/daemon_gate.mjs +55 -0
- package/scripts/utils/auth/daemon_gate.test.mjs +37 -0
- package/scripts/utils/auth/dev_key.mjs +163 -0
- package/scripts/utils/{auth_files.mjs → auth/files.mjs} +2 -4
- package/scripts/utils/auth/guided_pr_auth.mjs +79 -0
- package/scripts/utils/auth/guided_stack_web_login.mjs +75 -0
- package/scripts/utils/auth/handy_master_secret.mjs +68 -0
- package/scripts/utils/auth/interactive_stack_auth.mjs +72 -0
- package/scripts/utils/{auth_login_ux.mjs → auth/login_ux.mjs} +32 -13
- package/scripts/utils/auth/sources.mjs +38 -0
- package/scripts/utils/auth/stack_guided_login.mjs +353 -0
- package/scripts/utils/cli/cli_registry.mjs +24 -0
- package/scripts/utils/cli/cwd_scope.mjs +82 -0
- package/scripts/utils/cli/cwd_scope.test.mjs +77 -0
- package/scripts/utils/cli/flags.mjs +17 -0
- package/scripts/utils/cli/log_forwarder.mjs +157 -0
- package/scripts/utils/cli/normalize.mjs +16 -0
- package/scripts/utils/cli/prereqs.mjs +72 -0
- package/scripts/utils/cli/progress.mjs +126 -0
- package/scripts/utils/cli/smoke_help.mjs +2 -2
- package/scripts/utils/cli/verbosity.mjs +12 -0
- package/scripts/utils/cli/wizard.mjs +1 -1
- package/scripts/utils/crypto/tokens.mjs +14 -0
- package/scripts/utils/{dev_daemon.mjs → dev/daemon.mjs} +51 -7
- package/scripts/utils/dev/expo_dev.mjs +246 -0
- package/scripts/utils/dev/expo_dev.test.mjs +76 -0
- package/scripts/utils/{dev_server.mjs → dev/server.mjs} +22 -32
- package/scripts/utils/dev_auth_key.mjs +1 -1
- package/scripts/utils/{config.mjs → env/config.mjs} +3 -2
- package/scripts/utils/{dotenv.mjs → env/dotenv.mjs} +3 -0
- package/scripts/utils/{env.mjs → env/env.mjs} +5 -3
- package/scripts/utils/{env_file.mjs → env/env_file.mjs} +2 -1
- package/scripts/utils/{env_local.mjs → env/env_local.mjs} +1 -0
- package/scripts/utils/env/read.mjs +30 -0
- package/scripts/utils/env/values.mjs +13 -0
- package/scripts/utils/expo/command.mjs +52 -0
- package/scripts/utils/{expo.mjs → expo/expo.mjs} +23 -10
- package/scripts/utils/expo/metro_ports.mjs +114 -0
- package/scripts/utils/fs/json.mjs +25 -0
- package/scripts/utils/fs/ops.mjs +29 -0
- package/scripts/utils/fs/package_json.mjs +8 -0
- package/scripts/utils/fs/tail.mjs +12 -0
- package/scripts/utils/git/git.mjs +67 -0
- package/scripts/utils/git/refs.mjs +26 -0
- package/scripts/utils/{worktrees.mjs → git/worktrees.mjs} +27 -23
- package/scripts/utils/handy_master_secret.mjs +2 -2
- package/scripts/utils/mobile/config.mjs +31 -0
- package/scripts/utils/mobile/dev_client_links.mjs +60 -0
- package/scripts/utils/mobile/identifiers.mjs +47 -0
- package/scripts/utils/mobile/identifiers.test.mjs +42 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.mjs +128 -0
- package/scripts/utils/mobile/ios_xcodeproj_patch.test.mjs +98 -0
- package/scripts/utils/net/dns.mjs +10 -0
- package/scripts/utils/net/lan_ip.mjs +24 -0
- package/scripts/utils/{ports.mjs → net/ports.mjs} +12 -6
- package/scripts/utils/net/url.mjs +30 -0
- package/scripts/utils/net/url.test.mjs +20 -0
- package/scripts/utils/paths/localhost_host.mjs +56 -0
- package/scripts/utils/{paths.mjs → paths/paths.mjs} +52 -45
- package/scripts/utils/{runtime.mjs → paths/runtime.mjs} +3 -1
- package/scripts/utils/proc/commands.mjs +34 -0
- package/scripts/utils/{ownership.mjs → proc/ownership.mjs} +1 -1
- package/scripts/utils/proc/package_scripts.mjs +31 -0
- package/scripts/utils/proc/parallel.mjs +25 -0
- package/scripts/utils/proc/pids.mjs +11 -0
- package/scripts/utils/{pm.mjs → proc/pm.mjs} +128 -158
- package/scripts/utils/{proc.mjs → proc/proc.mjs} +77 -2
- package/scripts/utils/review/base_ref.mjs +74 -0
- package/scripts/utils/review/base_ref.test.mjs +54 -0
- package/scripts/utils/review/runners/coderabbit.mjs +19 -0
- package/scripts/utils/review/runners/codex.mjs +51 -0
- package/scripts/utils/review/targets.mjs +24 -0
- package/scripts/utils/review/targets.test.mjs +36 -0
- package/scripts/utils/sandbox/review_pr_sandbox.mjs +106 -0
- package/scripts/utils/{happy_server_infra.mjs → server/infra/happy_server_infra.mjs} +10 -49
- package/scripts/utils/server/mobile_api_url.mjs +61 -0
- package/scripts/utils/server/mobile_api_url.test.mjs +41 -0
- package/scripts/utils/server/port.mjs +68 -0
- package/scripts/utils/{server.mjs → server/server.mjs} +12 -0
- package/scripts/utils/server/urls.mjs +101 -0
- package/scripts/utils/server/validate.mjs +88 -0
- package/scripts/utils/service/autostart_darwin.mjs +182 -0
- package/scripts/utils/service/autostart_darwin.test.mjs +50 -0
- package/scripts/utils/stack/context.mjs +23 -0
- package/scripts/utils/stack/dirs.mjs +27 -0
- package/scripts/utils/stack/editor_workspace.mjs +152 -0
- package/scripts/utils/stack/names.mjs +12 -0
- package/scripts/utils/stack/pr_stack_name.mjs +16 -0
- package/scripts/utils/stack/runtime_state.mjs +88 -0
- package/scripts/utils/stack/stacks.mjs +45 -0
- package/scripts/utils/{stack_startup.mjs → stack/startup.mjs} +9 -2
- package/scripts/utils/{stack_stop.mjs → stack/stop.mjs} +24 -19
- package/scripts/utils/stack_context.mjs +3 -3
- package/scripts/utils/stack_runtime_state.mjs +1 -1
- package/scripts/utils/stacks.mjs +2 -2
- package/scripts/utils/{browser.mjs → ui/browser.mjs} +1 -1
- package/scripts/utils/ui/qr.mjs +17 -0
- package/scripts/utils/ui/text.mjs +16 -0
- package/scripts/utils/validate.mjs +1 -1
- package/scripts/where.mjs +6 -6
- package/scripts/worktrees.mjs +171 -113
- package/scripts/utils/auth_sources.mjs +0 -12
- package/scripts/utils/dev_expo_web.mjs +0 -112
- package/scripts/utils/localhost_host.mjs +0 -17
- package/scripts/utils/server_port.mjs +0 -9
- package/scripts/utils/server_urls.mjs +0 -54
- /package/scripts/utils/{sandbox.mjs → env/sandbox.mjs} +0 -0
- /package/scripts/utils/{fs.mjs → fs/fs.mjs} +0 -0
- /package/scripts/utils/{canonical_home.mjs → paths/canonical_home.mjs} +0 -0
- /package/scripts/utils/{watch.mjs → proc/watch.mjs} +0 -0
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { readdir } from 'node:fs/promises';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { getLegacyStorageRoot, getStacksStorageRoot } from '../paths/paths.mjs';
|
|
6
|
+
import { isSandboxed, sandboxAllowsGlobalSideEffects } from '../env/sandbox.mjs';
|
|
7
|
+
import { resolveStackEnvPath } from '../paths/paths.mjs';
|
|
8
|
+
|
|
9
|
+
export async function listAllStackNames() {
|
|
10
|
+
const names = new Set(['main']);
|
|
11
|
+
const allowLegacy = !isSandboxed() || sandboxAllowsGlobalSideEffects();
|
|
12
|
+
const roots = [
|
|
13
|
+
// New layout: ~/.happy/stacks/<name>/env
|
|
14
|
+
getStacksStorageRoot(),
|
|
15
|
+
// Legacy layout: ~/.happy/local/stacks/<name>/env
|
|
16
|
+
...(allowLegacy ? [join(getLegacyStorageRoot(), 'stacks')] : []),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const root of roots) {
|
|
20
|
+
let entries = [];
|
|
21
|
+
try {
|
|
22
|
+
// eslint-disable-next-line no-await-in-loop
|
|
23
|
+
entries = await readdir(root, { withFileTypes: true });
|
|
24
|
+
} catch {
|
|
25
|
+
entries = [];
|
|
26
|
+
}
|
|
27
|
+
for (const ent of entries) {
|
|
28
|
+
if (!ent.isDirectory()) continue;
|
|
29
|
+
const name = ent.name;
|
|
30
|
+
if (!name || name.startsWith('.')) continue;
|
|
31
|
+
const envPath = join(root, name, 'env');
|
|
32
|
+
if (existsSync(envPath)) {
|
|
33
|
+
names.add(name);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return Array.from(names).sort();
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function stackExistsSync(stackName) {
|
|
42
|
+
const name = String(stackName ?? '').trim() || 'main';
|
|
43
|
+
if (name === 'main') return true;
|
|
44
|
+
return existsSync(resolveStackEnvPath(name).envPath);
|
|
45
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { runCapture } from '
|
|
2
|
-
import { ensureDepsInstalled, pmExecBin } from '
|
|
1
|
+
import { runCapture } from '../proc/proc.mjs';
|
|
2
|
+
import { ensureDepsInstalled, pmExecBin } from '../proc/pm.mjs';
|
|
3
|
+
import { isSandboxed, sandboxAllowsGlobalSideEffects } from '../env/sandbox.mjs';
|
|
3
4
|
import { existsSync } from 'node:fs';
|
|
4
5
|
import { join } from 'node:path';
|
|
5
6
|
|
|
@@ -47,6 +48,12 @@ async function probeAccountCount({ serverDir, env }) {
|
|
|
47
48
|
}
|
|
48
49
|
|
|
49
50
|
export function resolveAutoCopyFromMainEnabled({ env, stackName, isInteractive }) {
|
|
51
|
+
// Sandboxes should be isolated by default.
|
|
52
|
+
// Auto auth seeding can copy credentials/account rows from another stack (global state),
|
|
53
|
+
// which breaks isolation and can confuse guided auth flows (setup-pr/review-pr).
|
|
54
|
+
if (isSandboxed() && !sandboxAllowsGlobalSideEffects()) {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
50
57
|
const raw = (env.HAPPY_STACKS_AUTO_AUTH_SEED ?? env.HAPPY_LOCAL_AUTO_AUTH_SEED ?? '').toString().trim();
|
|
51
58
|
if (raw) return raw !== '0';
|
|
52
59
|
|
|
@@ -2,19 +2,13 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { readdir, readFile } from 'node:fs/promises';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { getComponentDir } from '
|
|
6
|
-
import { isPidAlive, readPidState } from '
|
|
7
|
-
import { stopLocalDaemon } from '
|
|
8
|
-
import { stopHappyServerManagedInfra } from '
|
|
9
|
-
import { deleteStackRuntimeStateFile, getStackRuntimeStatePath, readStackRuntimeStateFile } from './
|
|
10
|
-
import { killPidOwnedByStack, killProcessGroupOwnedByStack, listPidsWithEnvNeedle } from '
|
|
11
|
-
|
|
12
|
-
function parseIntOrNull(raw) {
|
|
13
|
-
const s = String(raw ?? '').trim();
|
|
14
|
-
if (!s) return null;
|
|
15
|
-
const n = Number(s);
|
|
16
|
-
return Number.isFinite(n) && n > 0 ? n : null;
|
|
17
|
-
}
|
|
5
|
+
import { getComponentDir } from '../paths/paths.mjs';
|
|
6
|
+
import { isPidAlive, readPidState } from '../expo/expo.mjs';
|
|
7
|
+
import { stopLocalDaemon } from '../../daemon.mjs';
|
|
8
|
+
import { stopHappyServerManagedInfra } from '../server/infra/happy_server_infra.mjs';
|
|
9
|
+
import { deleteStackRuntimeStateFile, getStackRuntimeStatePath, readStackRuntimeStateFile } from './runtime_state.mjs';
|
|
10
|
+
import { killPidOwnedByStack, killProcessGroupOwnedByStack, listPidsWithEnvNeedle } from '../proc/ownership.mjs';
|
|
11
|
+
import { coercePort } from '../server/port.mjs';
|
|
18
12
|
|
|
19
13
|
function resolveServerComponentFromStackEnv(env) {
|
|
20
14
|
const v =
|
|
@@ -132,6 +126,7 @@ export async function stopStackWithEnv({ rootDir, stackName, baseDir, env, json,
|
|
|
132
126
|
daemonSessionsStopped: null,
|
|
133
127
|
daemonStopped: false,
|
|
134
128
|
killedPorts: [],
|
|
129
|
+
expoDev: [],
|
|
135
130
|
uiDev: [],
|
|
136
131
|
mobile: [],
|
|
137
132
|
infra: null,
|
|
@@ -139,10 +134,16 @@ export async function stopStackWithEnv({ rootDir, stackName, baseDir, env, json,
|
|
|
139
134
|
};
|
|
140
135
|
|
|
141
136
|
const serverComponent = resolveServerComponentFromStackEnv(env);
|
|
142
|
-
const port =
|
|
143
|
-
const backendPort =
|
|
137
|
+
const port = coercePort(env.HAPPY_STACKS_SERVER_PORT ?? env.HAPPY_LOCAL_SERVER_PORT);
|
|
138
|
+
const backendPort = coercePort(env.HAPPY_STACKS_HAPPY_SERVER_BACKEND_PORT ?? env.HAPPY_LOCAL_HAPPY_SERVER_BACKEND_PORT);
|
|
144
139
|
const cliHomeDir = (env.HAPPY_STACKS_CLI_HOME_DIR ?? env.HAPPY_LOCAL_CLI_HOME_DIR ?? join(baseDir, 'cli')).toString();
|
|
145
|
-
|
|
140
|
+
// IMPORTANT:
|
|
141
|
+
// When stopping a stack, always prefer the stack's pinned happy-cli checkout/worktree.
|
|
142
|
+
// Otherwise, PR stacks can accidentally run the base checkout's CLI bin, which may not be built
|
|
143
|
+
// (we intentionally skip building base checkouts in some sandbox PR flows).
|
|
144
|
+
const pinnedCliDir = (env.HAPPY_STACKS_COMPONENT_DIR_HAPPY_CLI ?? env.HAPPY_LOCAL_COMPONENT_DIR_HAPPY_CLI ?? '').toString().trim();
|
|
145
|
+
const cliDir = pinnedCliDir || getComponentDir(rootDir, 'happy-cli');
|
|
146
|
+
const cliBin = join(cliDir, 'bin', 'happy.mjs');
|
|
146
147
|
const envPath = (env.HAPPY_STACKS_ENV_FILE ?? env.HAPPY_LOCAL_ENV_FILE ?? '').toString();
|
|
147
148
|
|
|
148
149
|
// Preferred: stop stack-started processes (by PID) recorded in stack.runtime.json.
|
|
@@ -200,12 +201,16 @@ export async function stopStackWithEnv({ rootDir, stackName, baseDir, env, json,
|
|
|
200
201
|
}
|
|
201
202
|
|
|
202
203
|
try {
|
|
203
|
-
actions.
|
|
204
|
+
actions.expoDev = await stopExpoStateDir({ stackName, baseDir, kind: 'expo-dev', stateFileName: 'expo.state.json', envPath, json });
|
|
204
205
|
} catch (e) {
|
|
205
|
-
actions.errors.push({ step: 'expo-
|
|
206
|
+
actions.errors.push({ step: 'expo-dev', error: e instanceof Error ? e.message : String(e) });
|
|
206
207
|
}
|
|
207
208
|
try {
|
|
208
|
-
|
|
209
|
+
// Legacy cleanups (best-effort): older runs used separate state dirs.
|
|
210
|
+
actions.uiDev = await stopExpoStateDir({ stackName, baseDir, kind: 'ui-dev', stateFileName: 'ui.state.json', envPath, json });
|
|
211
|
+
const killedDev = await stopExpoStateDir({ stackName, baseDir, kind: 'mobile-dev', stateFileName: 'mobile.state.json', envPath, json });
|
|
212
|
+
const killedLegacy = await stopExpoStateDir({ stackName, baseDir, kind: 'mobile', stateFileName: 'expo.state.json', envPath, json });
|
|
213
|
+
actions.mobile = [...killedDev, ...killedLegacy];
|
|
209
214
|
} catch (e) {
|
|
210
215
|
actions.errors.push({ step: 'expo-mobile', error: e instanceof Error ? e.message : String(e) });
|
|
211
216
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { getStackName, resolveStackEnvPath } from './paths.mjs';
|
|
1
|
+
import { getStackName, resolveStackEnvPath } from './paths/paths.mjs';
|
|
2
2
|
import { getStackRuntimeStatePath } from './stack_runtime_state.mjs';
|
|
3
3
|
|
|
4
4
|
export function resolveStackContext({ env = process.env, autostart = null } = {}) {
|
|
5
5
|
const explicitStack = (env.HAPPY_STACKS_STACK ?? env.HAPPY_LOCAL_STACK ?? '').toString().trim();
|
|
6
|
-
const stackName = explicitStack || (autostart?.stackName ?? '') || getStackName();
|
|
6
|
+
const stackName = explicitStack || (autostart?.stackName ?? '') || getStackName(env);
|
|
7
7
|
const stackMode = Boolean(explicitStack);
|
|
8
8
|
|
|
9
9
|
const envPath =
|
|
10
10
|
(env.HAPPY_STACKS_ENV_FILE ?? env.HAPPY_LOCAL_ENV_FILE ?? '').toString().trim() ||
|
|
11
|
-
resolveStackEnvPath(stackName).envPath;
|
|
11
|
+
resolveStackEnvPath(stackName, env).envPath;
|
|
12
12
|
|
|
13
13
|
const runtimeStatePath =
|
|
14
14
|
(env.HAPPY_STACKS_RUNTIME_STATE_PATH ?? env.HAPPY_LOCAL_RUNTIME_STATE_PATH ?? '').toString().trim() ||
|
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { mkdir, readFile, rename, unlink, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname, join } from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { resolveStackEnvPath } from './paths.mjs';
|
|
5
|
+
import { resolveStackEnvPath } from './paths/paths.mjs';
|
|
6
6
|
|
|
7
7
|
export function getStackRuntimeStatePath(stackName) {
|
|
8
8
|
const { baseDir } = resolveStackEnvPath(stackName);
|
package/scripts/utils/stacks.mjs
CHANGED
|
@@ -2,8 +2,8 @@ import { readdir } from 'node:fs/promises';
|
|
|
2
2
|
import { existsSync } from 'node:fs';
|
|
3
3
|
import { join } from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { getLegacyStorageRoot, getStacksStorageRoot } from './paths.mjs';
|
|
6
|
-
import { isSandboxed, sandboxAllowsGlobalSideEffects } from './sandbox.mjs';
|
|
5
|
+
import { getLegacyStorageRoot, getStacksStorageRoot } from './paths/paths.mjs';
|
|
6
|
+
import { isSandboxed, sandboxAllowsGlobalSideEffects } from './env/sandbox.mjs';
|
|
7
7
|
|
|
8
8
|
export async function listAllStackNames() {
|
|
9
9
|
const names = new Set(['main']);
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import qrcodeTerminal from 'qrcode-terminal';
|
|
2
|
+
|
|
3
|
+
export async function renderQrAscii(text, { small = true } = {}) {
|
|
4
|
+
const qrText = String(text ?? '');
|
|
5
|
+
if (!qrText) return { ok: false, lines: [], error: 'empty QR payload' };
|
|
6
|
+
try {
|
|
7
|
+
const out = await new Promise((resolvePromise) => {
|
|
8
|
+
qrcodeTerminal.generate(qrText, { small: Boolean(small) }, (qr) => resolvePromise(String(qr ?? '')));
|
|
9
|
+
});
|
|
10
|
+
// Important: keep whitespace; scanners rely on quiet-zone padding.
|
|
11
|
+
const lines = String(out ?? '').replace(/\r/g, '').split('\n');
|
|
12
|
+
return { ok: true, lines, error: null };
|
|
13
|
+
} catch (e) {
|
|
14
|
+
return { ok: false, lines: [], error: e instanceof Error ? e.message : String(e) };
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export function stripAnsi(s) {
|
|
2
|
+
// eslint-disable-next-line no-control-regex
|
|
3
|
+
return String(s ?? '').replace(/\x1b\[[0-9;]*[A-Za-z]/g, '');
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function padRight(s, n) {
|
|
7
|
+
const str = String(s ?? '');
|
|
8
|
+
if (str.length >= n) return str.slice(0, n);
|
|
9
|
+
return str + ' '.repeat(n - str.length);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function parsePrefixedLabel(line) {
|
|
13
|
+
const m = String(line ?? '').match(/^\[([^\]]+)\]\s*/);
|
|
14
|
+
return m ? m[1] : null;
|
|
15
|
+
}
|
|
16
|
+
|
package/scripts/where.mjs
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import './utils/env.mjs';
|
|
1
|
+
import './utils/env/env.mjs';
|
|
2
2
|
|
|
3
3
|
import { existsSync } from 'node:fs';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
|
|
6
6
|
import { parseArgs } from './utils/cli/args.mjs';
|
|
7
|
-
import { expandHome } from './utils/canonical_home.mjs';
|
|
8
|
-
import { getComponentsDir, getComponentDir, getHappyStacksHomeDir, getRootDir, getStackLabel, getStackName, getWorkspaceDir, resolveStackEnvPath } from './utils/paths.mjs';
|
|
7
|
+
import { expandHome } from './utils/paths/canonical_home.mjs';
|
|
8
|
+
import { getComponentsDir, getComponentDir, getHappyStacksHomeDir, getRootDir, getStackLabel, getStackName, getWorkspaceDir, resolveStackEnvPath } from './utils/paths/paths.mjs';
|
|
9
9
|
import { printResult, wantsHelp, wantsJson } from './utils/cli/cli.mjs';
|
|
10
|
-
import { getRuntimeDir } from './utils/runtime.mjs';
|
|
11
|
-
import { getCanonicalHomeDir, getCanonicalHomeEnvPath } from './utils/config.mjs';
|
|
12
|
-
import { getSandboxDir } from './utils/sandbox.mjs';
|
|
10
|
+
import { getRuntimeDir } from './utils/paths/runtime.mjs';
|
|
11
|
+
import { getCanonicalHomeDir, getCanonicalHomeEnvPath } from './utils/env/config.mjs';
|
|
12
|
+
import { getSandboxDir } from './utils/env/sandbox.mjs';
|
|
13
13
|
|
|
14
14
|
function getHomeEnvPaths() {
|
|
15
15
|
const homeDir = getHappyStacksHomeDir();
|