@4pm/cli 0.4.1 → 0.4.2
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 +385 -183
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1345,7 +1345,7 @@ var require_react_development = __commonJS({
|
|
|
1345
1345
|
}
|
|
1346
1346
|
return dispatcher.useContext(Context);
|
|
1347
1347
|
}
|
|
1348
|
-
function
|
|
1348
|
+
function useState6(initialState) {
|
|
1349
1349
|
var dispatcher = resolveDispatcher();
|
|
1350
1350
|
return dispatcher.useState(initialState);
|
|
1351
1351
|
}
|
|
@@ -1373,7 +1373,7 @@ var require_react_development = __commonJS({
|
|
|
1373
1373
|
var dispatcher = resolveDispatcher();
|
|
1374
1374
|
return dispatcher.useCallback(callback, deps);
|
|
1375
1375
|
}
|
|
1376
|
-
function
|
|
1376
|
+
function useMemo4(create2, deps) {
|
|
1377
1377
|
var dispatcher = resolveDispatcher();
|
|
1378
1378
|
return dispatcher.useMemo(create2, deps);
|
|
1379
1379
|
}
|
|
@@ -2145,10 +2145,10 @@ var require_react_development = __commonJS({
|
|
|
2145
2145
|
exports.useImperativeHandle = useImperativeHandle;
|
|
2146
2146
|
exports.useInsertionEffect = useInsertionEffect;
|
|
2147
2147
|
exports.useLayoutEffect = useLayoutEffect2;
|
|
2148
|
-
exports.useMemo =
|
|
2148
|
+
exports.useMemo = useMemo4;
|
|
2149
2149
|
exports.useReducer = useReducer;
|
|
2150
2150
|
exports.useRef = useRef;
|
|
2151
|
-
exports.useState =
|
|
2151
|
+
exports.useState = useState6;
|
|
2152
2152
|
exports.useSyncExternalStore = useSyncExternalStore;
|
|
2153
2153
|
exports.useTransition = useTransition;
|
|
2154
2154
|
exports.version = ReactVersion;
|
|
@@ -7843,9 +7843,9 @@ var require_react_reconciler_development = __commonJS({
|
|
|
7843
7843
|
module.exports = function $$$reconciler($$$hostConfig) {
|
|
7844
7844
|
var exports2 = {};
|
|
7845
7845
|
"use strict";
|
|
7846
|
-
var
|
|
7846
|
+
var React17 = require_react();
|
|
7847
7847
|
var Scheduler = require_scheduler();
|
|
7848
|
-
var ReactSharedInternals =
|
|
7848
|
+
var ReactSharedInternals = React17.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
7849
7849
|
var suppressWarning = false;
|
|
7850
7850
|
function setSuppressWarning(newSuppressWarning) {
|
|
7851
7851
|
{
|
|
@@ -23490,7 +23490,7 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
23490
23490
|
if (process.env.NODE_ENV !== "production") {
|
|
23491
23491
|
(function() {
|
|
23492
23492
|
"use strict";
|
|
23493
|
-
var
|
|
23493
|
+
var React17 = require_react();
|
|
23494
23494
|
var REACT_ELEMENT_TYPE = /* @__PURE__ */ Symbol.for("react.element");
|
|
23495
23495
|
var REACT_PORTAL_TYPE = /* @__PURE__ */ Symbol.for("react.portal");
|
|
23496
23496
|
var REACT_FRAGMENT_TYPE = /* @__PURE__ */ Symbol.for("react.fragment");
|
|
@@ -23516,7 +23516,7 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
23516
23516
|
}
|
|
23517
23517
|
return null;
|
|
23518
23518
|
}
|
|
23519
|
-
var ReactSharedInternals =
|
|
23519
|
+
var ReactSharedInternals = React17.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED;
|
|
23520
23520
|
function error(format) {
|
|
23521
23521
|
{
|
|
23522
23522
|
{
|
|
@@ -24366,11 +24366,11 @@ var require_react_jsx_runtime_development = __commonJS({
|
|
|
24366
24366
|
return jsxWithValidation(type, props, key, false);
|
|
24367
24367
|
}
|
|
24368
24368
|
}
|
|
24369
|
-
var
|
|
24370
|
-
var
|
|
24369
|
+
var jsx8 = jsxWithValidationDynamic;
|
|
24370
|
+
var jsxs6 = jsxWithValidationStatic;
|
|
24371
24371
|
exports.Fragment = REACT_FRAGMENT_TYPE;
|
|
24372
|
-
exports.jsx =
|
|
24373
|
-
exports.jsxs =
|
|
24372
|
+
exports.jsx = jsx8;
|
|
24373
|
+
exports.jsxs = jsxs6;
|
|
24374
24374
|
})();
|
|
24375
24375
|
}
|
|
24376
24376
|
}
|
|
@@ -24404,7 +24404,14 @@ import { homedir as homedir2 } from "os";
|
|
|
24404
24404
|
import { join as join2 } from "path";
|
|
24405
24405
|
|
|
24406
24406
|
// src/config/profile.ts
|
|
24407
|
-
import {
|
|
24407
|
+
import {
|
|
24408
|
+
existsSync,
|
|
24409
|
+
mkdirSync,
|
|
24410
|
+
readdirSync,
|
|
24411
|
+
readFileSync,
|
|
24412
|
+
rmSync,
|
|
24413
|
+
writeFileSync
|
|
24414
|
+
} from "fs";
|
|
24408
24415
|
import { homedir } from "os";
|
|
24409
24416
|
import { join } from "path";
|
|
24410
24417
|
var DEFAULT_PROFILE = "default";
|
|
@@ -24443,6 +24450,18 @@ function resolveProfileArg(args) {
|
|
|
24443
24450
|
}
|
|
24444
24451
|
return [process.env.FOURPM_PROFILE ?? null, args];
|
|
24445
24452
|
}
|
|
24453
|
+
function listProfiles() {
|
|
24454
|
+
const base = join(homedir(), ".4pm", "profiles");
|
|
24455
|
+
if (!existsSync(base)) return [];
|
|
24456
|
+
try {
|
|
24457
|
+
return readdirSync(base, { withFileTypes: true }).filter((d) => d.isDirectory()).map((d) => ({
|
|
24458
|
+
name: d.name,
|
|
24459
|
+
linked: existsSync(join(base, d.name, "credential.cre"))
|
|
24460
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
24461
|
+
} catch {
|
|
24462
|
+
return [];
|
|
24463
|
+
}
|
|
24464
|
+
}
|
|
24446
24465
|
function profileDir(name) {
|
|
24447
24466
|
const dir = join(homedir(), ".4pm", "profiles", name);
|
|
24448
24467
|
mkdirSync(dir, { recursive: true });
|
|
@@ -24590,15 +24609,10 @@ async function fetchCliVersion(serverUrl) {
|
|
|
24590
24609
|
}
|
|
24591
24610
|
|
|
24592
24611
|
// src/commands/link.ts
|
|
24612
|
+
function sanitizeProfileName(name) {
|
|
24613
|
+
return name.replace(/[^a-zA-Z0-9._-]/g, "_") || "profile";
|
|
24614
|
+
}
|
|
24593
24615
|
async function runLink(serverUrl, explicitProfile) {
|
|
24594
|
-
const currentName = explicitProfile ?? defaultProfileName();
|
|
24595
|
-
if (readCredential(profileDir(currentName), currentName)) {
|
|
24596
|
-
const flag = explicitProfile ? ` --profile ${explicitProfile}` : "";
|
|
24597
|
-
console.log(
|
|
24598
|
-
`Profile "${currentName}" is already linked (.cre). Run \`4pm unlink${flag}\` first if you want to pair again.`
|
|
24599
|
-
);
|
|
24600
|
-
return;
|
|
24601
|
-
}
|
|
24602
24616
|
const hashcode1 = randomBytes(32).toString("hex");
|
|
24603
24617
|
console.log(`\u2500\u2500 4PM cli pairing (profile: ${explicitProfile ?? "default"}) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500`);
|
|
24604
24618
|
console.log("1. Open the 4PM web \u2192 /machines, enter hashcode (1):");
|
|
@@ -24609,12 +24623,13 @@ async function runLink(serverUrl, explicitProfile) {
|
|
|
24609
24623
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
24610
24624
|
const hashcode2 = (await rl.question("hashcode (2): ")).trim();
|
|
24611
24625
|
rl.close();
|
|
24612
|
-
const { hashcode3,
|
|
24626
|
+
const { hashcode3, username, scope } = await confirmPairing(
|
|
24613
24627
|
serverUrl,
|
|
24614
24628
|
hashcode2,
|
|
24615
24629
|
collectFingerprint()
|
|
24616
24630
|
);
|
|
24617
|
-
const profileName = explicitProfile ??
|
|
24631
|
+
const profileName = explicitProfile ?? sanitizeProfileName(username);
|
|
24632
|
+
const existed = readCredential(profileDir(profileName), profileName) != null;
|
|
24618
24633
|
writeCredential(profileDir(profileName), {
|
|
24619
24634
|
serverUrl,
|
|
24620
24635
|
hashcode3,
|
|
@@ -24622,10 +24637,16 @@ async function runLink(serverUrl, explicitProfile) {
|
|
|
24622
24637
|
// decided by the server from the pairing user (ADR-0010); shown in the TUI
|
|
24623
24638
|
pairedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
24624
24639
|
});
|
|
24625
|
-
|
|
24640
|
+
writeDefaultProfile(profileName);
|
|
24641
|
+
const verb = existed ? "Renewed" : "Added";
|
|
24626
24642
|
console.log(
|
|
24627
|
-
`\u2714
|
|
24643
|
+
`\u2714 ${verb} profile "${profileName}" as "${username}" \xB7 scope: ${scope} \u2014 saved .cre. Run \`4pm start\` to connect.`
|
|
24628
24644
|
);
|
|
24645
|
+
if (scope === "orchestrator") {
|
|
24646
|
+
console.log(
|
|
24647
|
+
" (This is the orchestrator/admin link. To pair a worker instead, pick the MACHINE user in the web pairing wizard before entering hashcode (1).)"
|
|
24648
|
+
);
|
|
24649
|
+
}
|
|
24629
24650
|
}
|
|
24630
24651
|
|
|
24631
24652
|
// src/commands/start.ts
|
|
@@ -24801,6 +24822,7 @@ async function checkAndUpdate(serverUrl, autoUpdate) {
|
|
|
24801
24822
|
|
|
24802
24823
|
// src/core/ws-client.ts
|
|
24803
24824
|
import { randomUUID } from "crypto";
|
|
24825
|
+
import { basename as basename3 } from "path";
|
|
24804
24826
|
|
|
24805
24827
|
// ../../22-packages/11-shared/51-utils/src/helpers.ts
|
|
24806
24828
|
function sleep(ms) {
|
|
@@ -25375,6 +25397,12 @@ function profileEnvVar(cmd) {
|
|
|
25375
25397
|
if (cmd.includes("codex")) return "CODEX_HOME";
|
|
25376
25398
|
return null;
|
|
25377
25399
|
}
|
|
25400
|
+
function profileLabels(config) {
|
|
25401
|
+
const cmd = config.aiCli ?? DEFAULT_AI_CLI;
|
|
25402
|
+
const envVar = profileEnvVar(cmd);
|
|
25403
|
+
const dirs = envVar === "CLAUDE_CONFIG_DIR" ? resolveDirs(config.claudeConfigDir) : envVar === "CODEX_HOME" ? resolveDirs(config.codexHome) : [];
|
|
25404
|
+
return dirs.map((dir) => basename(dir));
|
|
25405
|
+
}
|
|
25378
25406
|
function planAiRun(prompt, config, workingDir) {
|
|
25379
25407
|
const cmd = config.aiCli ?? DEFAULT_AI_CLI;
|
|
25380
25408
|
const args = [...config.aiArgs ?? DEFAULT_AI_ARGS[cmd] ?? [], prompt];
|
|
@@ -25858,6 +25886,13 @@ var WsClient = class {
|
|
|
25858
25886
|
this.bus.setStatus("connecting");
|
|
25859
25887
|
const token = await requestWsToken(this.credential.serverUrl, this.credential.hashcode3);
|
|
25860
25888
|
this.wsToken = token.wsToken;
|
|
25889
|
+
if (token.scope && token.scope !== this.credential.scope) {
|
|
25890
|
+
this.credential.scope = token.scope;
|
|
25891
|
+
writeCredential(this.context.profileDir, this.credential);
|
|
25892
|
+
this.bus.setScope(token.scope);
|
|
25893
|
+
} else if (token.scope) {
|
|
25894
|
+
this.bus.setScope(token.scope);
|
|
25895
|
+
}
|
|
25861
25896
|
const wsUrl = this.credential.serverUrl.replace(/^http/, "ws") + "/ws";
|
|
25862
25897
|
const socket = new wrapper_default(wsUrl);
|
|
25863
25898
|
this.socket = socket;
|
|
@@ -26088,7 +26123,10 @@ var WsClient = class {
|
|
|
26088
26123
|
})
|
|
26089
26124
|
});
|
|
26090
26125
|
this.bus.endBusy(plan.cmd);
|
|
26091
|
-
if (result.workedDir)
|
|
26126
|
+
if (result.workedDir) {
|
|
26127
|
+
setWorkingProfile(this.context.profileDir, plan.cmd, result.workedDir);
|
|
26128
|
+
this.bus.setActiveProfile(basename3(result.workedDir));
|
|
26129
|
+
}
|
|
26092
26130
|
this.send(WsChannels.COMMAND_OUTPUT, {
|
|
26093
26131
|
commandId,
|
|
26094
26132
|
seq: seq++,
|
|
@@ -26265,6 +26303,9 @@ function releaseInstanceLock(profileDir2) {
|
|
|
26265
26303
|
}
|
|
26266
26304
|
}
|
|
26267
26305
|
|
|
26306
|
+
// src/commands/start.ts
|
|
26307
|
+
import { basename as basename4 } from "path";
|
|
26308
|
+
|
|
26268
26309
|
// src/core/session-bus.ts
|
|
26269
26310
|
import { EventEmitter } from "events";
|
|
26270
26311
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
@@ -26276,6 +26317,10 @@ var SessionBus = class {
|
|
|
26276
26317
|
history = [];
|
|
26277
26318
|
/** Labels of in-flight commands (tool names) — top = the one shown as "processing". */
|
|
26278
26319
|
busyStack = [];
|
|
26320
|
+
/** The AI-CLI profile that last authenticated (shown in the header). */
|
|
26321
|
+
currentActiveProfile = null;
|
|
26322
|
+
/** Link scope learned from the server at connect time (heals the header/.cre). */
|
|
26323
|
+
currentScope = null;
|
|
26279
26324
|
constructor() {
|
|
26280
26325
|
this.emitter.setMaxListeners(50);
|
|
26281
26326
|
}
|
|
@@ -26329,6 +26374,34 @@ var SessionBus = class {
|
|
|
26329
26374
|
this.emitter.on("busy", fn);
|
|
26330
26375
|
return () => this.emitter.off("busy", fn);
|
|
26331
26376
|
}
|
|
26377
|
+
/** The AI-CLI profile currently in use (last authenticated), or null. */
|
|
26378
|
+
get activeProfile() {
|
|
26379
|
+
return this.currentActiveProfile;
|
|
26380
|
+
}
|
|
26381
|
+
/** Set the active AI-CLI profile (shown in the header). */
|
|
26382
|
+
setActiveProfile(label) {
|
|
26383
|
+
this.currentActiveProfile = label;
|
|
26384
|
+
this.emitter.emit("active-profile", label);
|
|
26385
|
+
}
|
|
26386
|
+
/** Subscribe to active-profile changes. */
|
|
26387
|
+
onActiveProfile(fn) {
|
|
26388
|
+
this.emitter.on("active-profile", fn);
|
|
26389
|
+
return () => this.emitter.off("active-profile", fn);
|
|
26390
|
+
}
|
|
26391
|
+
/** The link scope learned from the server, or null (fall back to the `.cre` value). */
|
|
26392
|
+
get scope() {
|
|
26393
|
+
return this.currentScope;
|
|
26394
|
+
}
|
|
26395
|
+
/** Set the link scope (learned at connect — heals the header). */
|
|
26396
|
+
setScope(scope) {
|
|
26397
|
+
this.currentScope = scope;
|
|
26398
|
+
this.emitter.emit("scope", scope);
|
|
26399
|
+
}
|
|
26400
|
+
/** Subscribe to scope changes. */
|
|
26401
|
+
onScope(fn) {
|
|
26402
|
+
this.emitter.on("scope", fn);
|
|
26403
|
+
return () => this.emitter.off("scope", fn);
|
|
26404
|
+
}
|
|
26332
26405
|
/**
|
|
26333
26406
|
* Subscribe to transcript entries. Any entries emitted before this subscription
|
|
26334
26407
|
* are replayed first (so a consumer that mounts a tick late — e.g. React effects —
|
|
@@ -31691,25 +31764,6 @@ var getInstance = (stdout, createInstance) => {
|
|
|
31691
31764
|
|
|
31692
31765
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/components/Static.js
|
|
31693
31766
|
var import_react11 = __toESM(require_react(), 1);
|
|
31694
|
-
function Static(props) {
|
|
31695
|
-
const { items, children: render2, style: customStyle } = props;
|
|
31696
|
-
const [index, setIndex] = (0, import_react11.useState)(0);
|
|
31697
|
-
const itemsToRender = (0, import_react11.useMemo)(() => {
|
|
31698
|
-
return items.slice(index);
|
|
31699
|
-
}, [items, index]);
|
|
31700
|
-
(0, import_react11.useLayoutEffect)(() => {
|
|
31701
|
-
setIndex(items.length);
|
|
31702
|
-
}, [items.length]);
|
|
31703
|
-
const children = itemsToRender.map((item, itemIndex) => {
|
|
31704
|
-
return render2(item, index + itemIndex);
|
|
31705
|
-
});
|
|
31706
|
-
const style = (0, import_react11.useMemo)(() => ({
|
|
31707
|
-
position: "absolute",
|
|
31708
|
-
flexDirection: "column",
|
|
31709
|
-
...customStyle
|
|
31710
|
-
}), [customStyle]);
|
|
31711
|
-
return import_react11.default.createElement("ink-box", { internal_static: true, style }, children);
|
|
31712
|
-
}
|
|
31713
31767
|
|
|
31714
31768
|
// ../../node_modules/.pnpm/ink@5.2.1_@types+react@18.3.31_react@18.3.1/node_modules/ink/build/components/Transform.js
|
|
31715
31769
|
var import_react12 = __toESM(require_react(), 1);
|
|
@@ -32035,12 +32089,22 @@ function appendInputHistory(profileDir2, line, current) {
|
|
|
32035
32089
|
// src/ui/banner.tsx
|
|
32036
32090
|
var import_react22 = __toESM(require_react(), 1);
|
|
32037
32091
|
var import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
32092
|
+
function dotColor(status) {
|
|
32093
|
+
if (status === "connected") return "green";
|
|
32094
|
+
if (status === "stopped") return "red";
|
|
32095
|
+
return "yellow";
|
|
32096
|
+
}
|
|
32038
32097
|
function scopeLabel(scope) {
|
|
32039
32098
|
if (scope === "project") return "project (MACHINE)";
|
|
32040
32099
|
if (scope === "orchestrator") return "orchestrator (root)";
|
|
32041
32100
|
return scope;
|
|
32042
32101
|
}
|
|
32043
|
-
function Banner({
|
|
32102
|
+
function Banner({
|
|
32103
|
+
info,
|
|
32104
|
+
scope,
|
|
32105
|
+
status,
|
|
32106
|
+
activeProfile
|
|
32107
|
+
}) {
|
|
32044
32108
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { flexDirection: "row", paddingX: 1, paddingTop: 1, children: [
|
|
32045
32109
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Box_default, { marginRight: 2, flexDirection: "column", children: [
|
|
32046
32110
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", bold: true, children: "\u250C\u2500\u2510" }),
|
|
@@ -32057,20 +32121,32 @@ function Banner({ info }) {
|
|
|
32057
32121
|
] }),
|
|
32058
32122
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
|
|
32059
32123
|
"scope: ",
|
|
32060
|
-
scopeLabel(
|
|
32124
|
+
scopeLabel(scope),
|
|
32061
32125
|
" \xB7 profile: ",
|
|
32062
32126
|
info.profile
|
|
32063
32127
|
] }),
|
|
32064
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
|
|
32128
|
+
scope === "orchestrator" ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
|
|
32065
32129
|
"server: ",
|
|
32066
|
-
info.serverUrl
|
|
32067
|
-
|
|
32068
|
-
|
|
32069
|
-
|
|
32070
|
-
|
|
32071
|
-
|
|
32072
|
-
"serving: ",
|
|
32130
|
+
info.serverUrl,
|
|
32131
|
+
" \xB7 mode: orchestrator (short-lived AI)"
|
|
32132
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { dimColor: true, children: [
|
|
32133
|
+
"server: ",
|
|
32134
|
+
info.serverUrl,
|
|
32135
|
+
" \xB7 serving: ",
|
|
32073
32136
|
info.physicPath ?? "(no physic project)"
|
|
32137
|
+
] }),
|
|
32138
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
32139
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: dotColor(status), children: "\u25CF" }),
|
|
32140
|
+
" ",
|
|
32141
|
+
status,
|
|
32142
|
+
" \xB7 using",
|
|
32143
|
+
" ",
|
|
32144
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: info.aiCli }),
|
|
32145
|
+
activeProfile ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Text, { children: [
|
|
32146
|
+
" ",
|
|
32147
|
+
"\xB7 profile ",
|
|
32148
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { color: "cyan", children: activeProfile })
|
|
32149
|
+
] }) : null
|
|
32074
32150
|
] })
|
|
32075
32151
|
] })
|
|
32076
32152
|
] });
|
|
@@ -32123,93 +32199,6 @@ function TranscriptLine({ line }) {
|
|
|
32123
32199
|
|
|
32124
32200
|
// src/ui/command-input.tsx
|
|
32125
32201
|
var import_react24 = __toESM(require_react(), 1);
|
|
32126
|
-
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
32127
|
-
function dotColor(status) {
|
|
32128
|
-
if (status === "connected") return "green";
|
|
32129
|
-
if (status === "stopped") return "red";
|
|
32130
|
-
return "yellow";
|
|
32131
|
-
}
|
|
32132
|
-
function StatusHint({
|
|
32133
|
-
status,
|
|
32134
|
-
aiCli: aiCli2
|
|
32135
|
-
}) {
|
|
32136
|
-
const connected = status === "connected";
|
|
32137
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
|
|
32138
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: dotColor(status), children: "\u25CF" }),
|
|
32139
|
-
" ",
|
|
32140
|
-
status,
|
|
32141
|
-
connected ? ` \xB7 prompt \u2192 ${aiCli2} \xB7 /help \xB7 \u2191\u2193 history \xB7 Ctrl+C quit` : " \xB7 waiting for connection\u2026"
|
|
32142
|
-
] });
|
|
32143
|
-
}
|
|
32144
|
-
function CommandInput({
|
|
32145
|
-
status,
|
|
32146
|
-
history,
|
|
32147
|
-
onSubmit
|
|
32148
|
-
}) {
|
|
32149
|
-
const { exit } = use_app_default();
|
|
32150
|
-
const [value, setValue] = (0, import_react24.useState)("");
|
|
32151
|
-
const [navIndex, setNavIndex] = (0, import_react24.useState)(null);
|
|
32152
|
-
const connected = status === "connected";
|
|
32153
|
-
use_input_default((input, key) => {
|
|
32154
|
-
if (key.ctrl && (input === "c" || input === "d")) {
|
|
32155
|
-
exit();
|
|
32156
|
-
return;
|
|
32157
|
-
}
|
|
32158
|
-
if (key.upArrow) {
|
|
32159
|
-
if (history.length === 0) return;
|
|
32160
|
-
const next = navIndex === null ? history.length - 1 : Math.max(0, navIndex - 1);
|
|
32161
|
-
setNavIndex(next);
|
|
32162
|
-
setValue(history[next] ?? "");
|
|
32163
|
-
return;
|
|
32164
|
-
}
|
|
32165
|
-
if (key.downArrow) {
|
|
32166
|
-
if (navIndex === null) return;
|
|
32167
|
-
const next = navIndex + 1;
|
|
32168
|
-
if (next >= history.length) {
|
|
32169
|
-
setNavIndex(null);
|
|
32170
|
-
setValue("");
|
|
32171
|
-
} else {
|
|
32172
|
-
setNavIndex(next);
|
|
32173
|
-
setValue(history[next] ?? "");
|
|
32174
|
-
}
|
|
32175
|
-
return;
|
|
32176
|
-
}
|
|
32177
|
-
if (key.return) {
|
|
32178
|
-
const trimmed = value.trim();
|
|
32179
|
-
if (trimmed.length > 0) onSubmit(trimmed);
|
|
32180
|
-
setValue("");
|
|
32181
|
-
setNavIndex(null);
|
|
32182
|
-
return;
|
|
32183
|
-
}
|
|
32184
|
-
if (key.backspace || key.delete) {
|
|
32185
|
-
setValue((v) => v.slice(0, -1));
|
|
32186
|
-
setNavIndex(null);
|
|
32187
|
-
return;
|
|
32188
|
-
}
|
|
32189
|
-
if (!key.ctrl && !key.meta && input) {
|
|
32190
|
-
setValue((v) => v + input);
|
|
32191
|
-
setNavIndex(null);
|
|
32192
|
-
}
|
|
32193
|
-
});
|
|
32194
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
|
|
32195
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: connected ? "cyan" : "gray", children: "\u203A " }),
|
|
32196
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: value }),
|
|
32197
|
-
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { inverse: true, children: " " })
|
|
32198
|
-
] });
|
|
32199
|
-
}
|
|
32200
|
-
|
|
32201
|
-
// src/ui/spinner.tsx
|
|
32202
|
-
var import_react25 = __toESM(require_react(), 1);
|
|
32203
|
-
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
32204
|
-
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
32205
|
-
function Spinner() {
|
|
32206
|
-
const [index, setIndex] = (0, import_react25.useState)(0);
|
|
32207
|
-
(0, import_react25.useEffect)(() => {
|
|
32208
|
-
const timer = setInterval(() => setIndex((i) => (i + 1) % FRAMES.length), 90);
|
|
32209
|
-
return () => clearInterval(timer);
|
|
32210
|
-
}, []);
|
|
32211
|
-
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: FRAMES[index] });
|
|
32212
|
-
}
|
|
32213
32202
|
|
|
32214
32203
|
// src/ui/slash-commands.ts
|
|
32215
32204
|
import { existsSync as existsSync9 } from "fs";
|
|
@@ -32354,6 +32343,19 @@ var COMMANDS = [
|
|
|
32354
32343
|
description: "View / init / update / delete profile config",
|
|
32355
32344
|
run: runConfig
|
|
32356
32345
|
},
|
|
32346
|
+
{
|
|
32347
|
+
name: "claude-cmd",
|
|
32348
|
+
usage: "/claude-cmd /context",
|
|
32349
|
+
description: "Run an AI-CLI slash command (e.g. /context, /usage, /status)",
|
|
32350
|
+
run: (ctx) => {
|
|
32351
|
+
const rest = ctx.args.join(" ").trim();
|
|
32352
|
+
if (!rest) {
|
|
32353
|
+
ctx.print("usage: /claude-cmd /context (forwards to the AI CLI)", "error");
|
|
32354
|
+
return;
|
|
32355
|
+
}
|
|
32356
|
+
ctx.submitAi(rest);
|
|
32357
|
+
}
|
|
32358
|
+
},
|
|
32357
32359
|
{
|
|
32358
32360
|
name: "clear",
|
|
32359
32361
|
usage: "/clear",
|
|
@@ -32368,6 +32370,7 @@ var COMMANDS = [
|
|
|
32368
32370
|
}
|
|
32369
32371
|
];
|
|
32370
32372
|
var ALIASES = { exit: "quit" };
|
|
32373
|
+
var SLASH_COMMANDS = COMMANDS.map((c) => ({ name: c.name, usage: c.usage, description: c.description }));
|
|
32371
32374
|
function runSlashCommand(line, base) {
|
|
32372
32375
|
const tokens = line.replace(/^\//, "").trim().split(/\s+/);
|
|
32373
32376
|
const rawName = tokens[0] ?? "";
|
|
@@ -32381,10 +32384,121 @@ function runSlashCommand(line, base) {
|
|
|
32381
32384
|
command.run(ctx);
|
|
32382
32385
|
}
|
|
32383
32386
|
|
|
32387
|
+
// src/ui/command-input.tsx
|
|
32388
|
+
var import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
32389
|
+
function Hint() {
|
|
32390
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: " /help \xB7 Tab complete \xB7 \u2191\u2193 history \xB7 PgUp/PgDn scroll \xB7 Ctrl+C quit" });
|
|
32391
|
+
}
|
|
32392
|
+
function CommandInput({
|
|
32393
|
+
status,
|
|
32394
|
+
history,
|
|
32395
|
+
onSubmit
|
|
32396
|
+
}) {
|
|
32397
|
+
const { exit } = use_app_default();
|
|
32398
|
+
const [value, setValue] = (0, import_react24.useState)("");
|
|
32399
|
+
const [navIndex, setNavIndex] = (0, import_react24.useState)(null);
|
|
32400
|
+
const [sugIndex, setSugIndex] = (0, import_react24.useState)(0);
|
|
32401
|
+
const connected = status === "connected";
|
|
32402
|
+
const matching = value.startsWith("/") && !value.includes(" ") ? SLASH_COMMANDS.filter((c) => c.name.startsWith(value.slice(1))) : [];
|
|
32403
|
+
const menuOpen = matching.length > 0;
|
|
32404
|
+
const selected = matching[Math.min(sugIndex, matching.length - 1)];
|
|
32405
|
+
use_input_default((input, key) => {
|
|
32406
|
+
if (key.ctrl && (input === "c" || input === "d")) {
|
|
32407
|
+
exit();
|
|
32408
|
+
return;
|
|
32409
|
+
}
|
|
32410
|
+
if (key.tab && selected) {
|
|
32411
|
+
setValue(`/${selected.name} `);
|
|
32412
|
+
setSugIndex(0);
|
|
32413
|
+
setNavIndex(null);
|
|
32414
|
+
return;
|
|
32415
|
+
}
|
|
32416
|
+
if (key.upArrow) {
|
|
32417
|
+
if (menuOpen) {
|
|
32418
|
+
setSugIndex((i) => (i - 1 + matching.length) % matching.length);
|
|
32419
|
+
return;
|
|
32420
|
+
}
|
|
32421
|
+
if (history.length === 0) return;
|
|
32422
|
+
const next = navIndex === null ? history.length - 1 : Math.max(0, navIndex - 1);
|
|
32423
|
+
setNavIndex(next);
|
|
32424
|
+
setValue(history[next] ?? "");
|
|
32425
|
+
return;
|
|
32426
|
+
}
|
|
32427
|
+
if (key.downArrow) {
|
|
32428
|
+
if (menuOpen) {
|
|
32429
|
+
setSugIndex((i) => (i + 1) % matching.length);
|
|
32430
|
+
return;
|
|
32431
|
+
}
|
|
32432
|
+
if (navIndex === null) return;
|
|
32433
|
+
const next = navIndex + 1;
|
|
32434
|
+
if (next >= history.length) {
|
|
32435
|
+
setNavIndex(null);
|
|
32436
|
+
setValue("");
|
|
32437
|
+
} else {
|
|
32438
|
+
setNavIndex(next);
|
|
32439
|
+
setValue(history[next] ?? "");
|
|
32440
|
+
}
|
|
32441
|
+
return;
|
|
32442
|
+
}
|
|
32443
|
+
if (key.return) {
|
|
32444
|
+
const trimmed = value.trim();
|
|
32445
|
+
if (trimmed.length > 0) onSubmit(trimmed);
|
|
32446
|
+
setValue("");
|
|
32447
|
+
setNavIndex(null);
|
|
32448
|
+
setSugIndex(0);
|
|
32449
|
+
return;
|
|
32450
|
+
}
|
|
32451
|
+
if (key.backspace || key.delete) {
|
|
32452
|
+
setValue((v) => v.slice(0, -1));
|
|
32453
|
+
setNavIndex(null);
|
|
32454
|
+
setSugIndex(0);
|
|
32455
|
+
return;
|
|
32456
|
+
}
|
|
32457
|
+
if (!key.ctrl && !key.meta && input) {
|
|
32458
|
+
setValue((v) => v + input);
|
|
32459
|
+
setNavIndex(null);
|
|
32460
|
+
setSugIndex(0);
|
|
32461
|
+
}
|
|
32462
|
+
});
|
|
32463
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { flexDirection: "column", children: [
|
|
32464
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { children: menuOpen ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { children: [
|
|
32465
|
+
matching.map((c, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: i === sugIndex ? "cyan" : "gray", inverse: i === sugIndex, children: [
|
|
32466
|
+
" ",
|
|
32467
|
+
"/",
|
|
32468
|
+
c.name,
|
|
32469
|
+
" "
|
|
32470
|
+
] }, c.name)),
|
|
32471
|
+
selected ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
|
|
32472
|
+
" \u2014 ",
|
|
32473
|
+
selected.description,
|
|
32474
|
+
" (Tab)"
|
|
32475
|
+
] }) : null
|
|
32476
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: " " }) }),
|
|
32477
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { children: [
|
|
32478
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { color: connected ? "cyan" : "gray", children: "\u203A " }),
|
|
32479
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: value }),
|
|
32480
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { inverse: true, children: " " })
|
|
32481
|
+
] })
|
|
32482
|
+
] });
|
|
32483
|
+
}
|
|
32484
|
+
|
|
32485
|
+
// src/ui/spinner.tsx
|
|
32486
|
+
var import_react25 = __toESM(require_react(), 1);
|
|
32487
|
+
var import_jsx_runtime4 = __toESM(require_jsx_runtime(), 1);
|
|
32488
|
+
var FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
32489
|
+
function Spinner() {
|
|
32490
|
+
const [index, setIndex] = (0, import_react25.useState)(0);
|
|
32491
|
+
(0, import_react25.useEffect)(() => {
|
|
32492
|
+
const timer = setInterval(() => setIndex((i) => (i + 1) % FRAMES.length), 90);
|
|
32493
|
+
return () => clearInterval(timer);
|
|
32494
|
+
}, []);
|
|
32495
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Text, { color: "yellow", children: FRAMES[index] });
|
|
32496
|
+
}
|
|
32497
|
+
|
|
32384
32498
|
// src/ui/app.tsx
|
|
32385
32499
|
var import_jsx_runtime5 = __toESM(require_jsx_runtime(), 1);
|
|
32386
|
-
var
|
|
32387
|
-
var
|
|
32500
|
+
var CHROME_ROWS = 12;
|
|
32501
|
+
var ENTRY_CAP = 3e3;
|
|
32388
32502
|
function Rule({ width }) {
|
|
32389
32503
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: "\u2500".repeat(Math.max(10, width)) });
|
|
32390
32504
|
}
|
|
@@ -32410,24 +32524,30 @@ function App2({
|
|
|
32410
32524
|
const [entries2, setEntries] = (0, import_react26.useState)([]);
|
|
32411
32525
|
const [status, setStatus] = (0, import_react26.useState)(bus.status);
|
|
32412
32526
|
const [busy, setBusy] = (0, import_react26.useState)(bus.busy);
|
|
32527
|
+
const [activeProfile, setActiveProfile] = (0, import_react26.useState)(bus.activeProfile);
|
|
32528
|
+
const [scope, setScope] = (0, import_react26.useState)(bus.scope ?? info.scope);
|
|
32529
|
+
const [scrollBack, setScrollBack] = (0, import_react26.useState)(0);
|
|
32413
32530
|
const [history, setHistory] = (0, import_react26.useState)(() => loadInputHistory(info.profileDir));
|
|
32414
|
-
const [staticKey, setStaticKey] = (0, import_react26.useState)(0);
|
|
32415
|
-
const clearTranscript = () => {
|
|
32416
|
-
setEntries([]);
|
|
32417
|
-
setStaticKey((k) => k + 1);
|
|
32418
|
-
stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
32419
|
-
};
|
|
32420
32531
|
(0, import_react26.useEffect)(() => {
|
|
32421
|
-
const offTranscript = bus.onTranscript(
|
|
32532
|
+
const offTranscript = bus.onTranscript(
|
|
32533
|
+
(entry) => setEntries(
|
|
32534
|
+
(prev) => prev.length >= ENTRY_CAP ? [...prev.slice(prev.length - ENTRY_CAP + 1), entry] : [...prev, entry]
|
|
32535
|
+
)
|
|
32536
|
+
);
|
|
32422
32537
|
const offStatus = bus.onStatus(setStatus);
|
|
32423
32538
|
const offBusy = bus.onBusy(setBusy);
|
|
32539
|
+
const offProfile = bus.onActiveProfile(setActiveProfile);
|
|
32540
|
+
const offScope = bus.onScope(setScope);
|
|
32424
32541
|
return () => {
|
|
32425
32542
|
offTranscript();
|
|
32426
32543
|
offStatus();
|
|
32427
32544
|
offBusy();
|
|
32545
|
+
offProfile();
|
|
32546
|
+
offScope();
|
|
32428
32547
|
};
|
|
32429
32548
|
}, [bus]);
|
|
32430
32549
|
const handleSubmit = (input) => {
|
|
32550
|
+
setScrollBack(0);
|
|
32431
32551
|
setHistory((prev) => appendInputHistory(info.profileDir, input, prev));
|
|
32432
32552
|
const trimmed = input.trim();
|
|
32433
32553
|
if (trimmed.startsWith("/")) {
|
|
@@ -32435,36 +32555,47 @@ function App2({
|
|
|
32435
32555
|
runSlashCommand(trimmed, {
|
|
32436
32556
|
info,
|
|
32437
32557
|
print: (text, level) => bus.push({ source: "local", kind: "log", text, level }),
|
|
32438
|
-
clear:
|
|
32439
|
-
|
|
32558
|
+
clear: () => {
|
|
32559
|
+
setEntries([]);
|
|
32560
|
+
setScrollBack(0);
|
|
32561
|
+
stdout.write("\x1B[2J\x1B[3J\x1B[H");
|
|
32562
|
+
},
|
|
32563
|
+
quit: () => exit(),
|
|
32564
|
+
submitAi: (aiInput) => bus.submitLocal(aiInput)
|
|
32440
32565
|
});
|
|
32441
32566
|
return;
|
|
32442
32567
|
}
|
|
32443
32568
|
bus.submitLocal(input);
|
|
32444
32569
|
};
|
|
32445
|
-
const
|
|
32446
|
-
const
|
|
32447
|
-
|
|
32448
|
-
|
|
32449
|
-
|
|
32570
|
+
const bodyHeight = Math.max(3, rows - CHROME_ROWS);
|
|
32571
|
+
const lines = (0, import_react26.useMemo)(() => flattenEntries(entries2), [entries2]);
|
|
32572
|
+
const maxScroll = Math.max(0, lines.length - bodyHeight);
|
|
32573
|
+
const clampedBack = Math.min(scrollBack, maxScroll);
|
|
32574
|
+
const lineRows = clampedBack > 0 ? bodyHeight - 1 : bodyHeight;
|
|
32575
|
+
const start = Math.max(0, lines.length - lineRows - clampedBack);
|
|
32576
|
+
const visible = lines.slice(start, start + lineRows);
|
|
32450
32577
|
const width = cols - 1;
|
|
32451
|
-
|
|
32452
|
-
|
|
32453
|
-
|
|
32454
|
-
|
|
32455
|
-
|
|
32456
|
-
|
|
32457
|
-
|
|
32458
|
-
|
|
32459
|
-
|
|
32460
|
-
|
|
32461
|
-
|
|
32462
|
-
|
|
32463
|
-
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
|
|
32464
|
-
|
|
32465
|
-
|
|
32466
|
-
|
|
32467
|
-
] })
|
|
32578
|
+
use_input_default((_input, key) => {
|
|
32579
|
+
if (key.pageUp) setScrollBack((b) => Math.min(maxScroll, b + bodyHeight));
|
|
32580
|
+
else if (key.pageDown) setScrollBack((b) => Math.max(0, b - bodyHeight));
|
|
32581
|
+
});
|
|
32582
|
+
return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", height: rows, children: [
|
|
32583
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Banner, { info, scope, status, activeProfile }),
|
|
32584
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Rule, { width }),
|
|
32585
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Box_default, { flexDirection: "column", height: bodyHeight, overflow: "hidden", children: [
|
|
32586
|
+
visible.map((line) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(TranscriptLine, { line }, line.key)),
|
|
32587
|
+
clampedBack > 0 ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { dimColor: true, children: ` \u2191 ${clampedBack} newer line(s) hidden \xB7 PageDown` }) : null
|
|
32588
|
+
] }),
|
|
32589
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Box_default, { justifyContent: "flex-end", children: busy ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(Text, { color: "yellow", children: [
|
|
32590
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Spinner, {}),
|
|
32591
|
+
" processing \xB7 ",
|
|
32592
|
+
busy,
|
|
32593
|
+
"\u2026"
|
|
32594
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Text, { children: " " }) }),
|
|
32595
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Rule, { width }),
|
|
32596
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(CommandInput, { status, history, onSubmit: handleSubmit }),
|
|
32597
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Rule, { width }),
|
|
32598
|
+
/* @__PURE__ */ (0, import_jsx_runtime5.jsx)(Hint, {})
|
|
32468
32599
|
] });
|
|
32469
32600
|
}
|
|
32470
32601
|
|
|
@@ -32550,6 +32681,10 @@ async function runStart(profileDir2, profileName) {
|
|
|
32550
32681
|
physicPath: config.physicPath ?? null,
|
|
32551
32682
|
aiCli: config.aiCli ?? "claude"
|
|
32552
32683
|
};
|
|
32684
|
+
const aiCli2 = config.aiCli ?? "claude";
|
|
32685
|
+
const workingDir = getWorkingProfile(profileDir2, aiCli2);
|
|
32686
|
+
const labels = profileLabels(config);
|
|
32687
|
+
bus.setActiveProfile(workingDir ? basename4(workingDir) : labels[0] ?? null);
|
|
32553
32688
|
const tui = runTui(bus, info);
|
|
32554
32689
|
bus.log(`Connecting to ${credential.serverUrl} (profile: ${profileName})\u2026`);
|
|
32555
32690
|
void client.run();
|
|
@@ -32616,11 +32751,68 @@ async function runVersion(profileDir2, profileName, serverUrlFlag) {
|
|
|
32616
32751
|
}
|
|
32617
32752
|
}
|
|
32618
32753
|
|
|
32754
|
+
// src/ui/select-profile.tsx
|
|
32755
|
+
var import_react28 = __toESM(require_react(), 1);
|
|
32756
|
+
var import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
32757
|
+
function Picker({
|
|
32758
|
+
items,
|
|
32759
|
+
title,
|
|
32760
|
+
onDone
|
|
32761
|
+
}) {
|
|
32762
|
+
const { exit } = use_app_default();
|
|
32763
|
+
const [index, setIndex] = (0, import_react28.useState)(0);
|
|
32764
|
+
use_input_default((input, key) => {
|
|
32765
|
+
if (key.upArrow) setIndex((i) => (i - 1 + items.length) % items.length);
|
|
32766
|
+
else if (key.downArrow) setIndex((i) => (i + 1) % items.length);
|
|
32767
|
+
else if (key.return) {
|
|
32768
|
+
onDone(items[index]?.value ?? null);
|
|
32769
|
+
exit();
|
|
32770
|
+
} else if (key.escape || key.ctrl && input === "c") {
|
|
32771
|
+
onDone(null);
|
|
32772
|
+
exit();
|
|
32773
|
+
}
|
|
32774
|
+
});
|
|
32775
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { flexDirection: "column", paddingY: 1, children: [
|
|
32776
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { bold: true, children: title }),
|
|
32777
|
+
items.map((item, i) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: i === index ? "cyan" : void 0, children: [
|
|
32778
|
+
i === index ? "\u276F " : " ",
|
|
32779
|
+
item.label
|
|
32780
|
+
] }, item.value)),
|
|
32781
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { dimColor: true, children: "\u2191\u2193 move \xB7 Enter select \xB7 Esc cancel" })
|
|
32782
|
+
] });
|
|
32783
|
+
}
|
|
32784
|
+
function selectFromList(items, title) {
|
|
32785
|
+
return new Promise((resolve5) => {
|
|
32786
|
+
let chosen = null;
|
|
32787
|
+
const app = render_default(/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Picker, { items, title, onDone: (v) => chosen = v }));
|
|
32788
|
+
void app.waitUntilExit().then(() => resolve5(chosen));
|
|
32789
|
+
});
|
|
32790
|
+
}
|
|
32791
|
+
|
|
32619
32792
|
// src/index.ts
|
|
32620
32793
|
function flagValue(args, name) {
|
|
32621
32794
|
const index = args.indexOf(`--${name}`);
|
|
32622
32795
|
return index >= 0 ? args[index + 1] : void 0;
|
|
32623
32796
|
}
|
|
32797
|
+
async function pickLinkedProfile(explicit, action) {
|
|
32798
|
+
if (explicit) return explicit;
|
|
32799
|
+
const linked = listProfiles().filter((p) => p.linked);
|
|
32800
|
+
if (linked.length === 0) {
|
|
32801
|
+
console.error("No linked profile \u2014 run `4pm link` first.");
|
|
32802
|
+
return null;
|
|
32803
|
+
}
|
|
32804
|
+
if (linked.length === 1) return linked[0].name;
|
|
32805
|
+
if (!process.stdout.isTTY) {
|
|
32806
|
+
console.error(
|
|
32807
|
+
`Multiple profiles \u2014 pass --profile <name> (or run \`4pm ${action}\` in a terminal to pick).`
|
|
32808
|
+
);
|
|
32809
|
+
return null;
|
|
32810
|
+
}
|
|
32811
|
+
return selectFromList(
|
|
32812
|
+
linked.map((p) => ({ label: p.name, value: p.name })),
|
|
32813
|
+
`Select a profile to ${action}:`
|
|
32814
|
+
);
|
|
32815
|
+
}
|
|
32624
32816
|
async function main() {
|
|
32625
32817
|
const [explicitProfile, args] = resolveProfileArg(process.argv.slice(2));
|
|
32626
32818
|
const [command, ...rest] = args;
|
|
@@ -32631,12 +32823,20 @@ async function main() {
|
|
|
32631
32823
|
break;
|
|
32632
32824
|
}
|
|
32633
32825
|
case "start": {
|
|
32634
|
-
const name = explicitProfile
|
|
32826
|
+
const name = await pickLinkedProfile(explicitProfile, "start");
|
|
32827
|
+
if (!name) {
|
|
32828
|
+
process.exitCode = 1;
|
|
32829
|
+
break;
|
|
32830
|
+
}
|
|
32635
32831
|
await runStart(profileDir(name), name);
|
|
32636
32832
|
break;
|
|
32637
32833
|
}
|
|
32638
32834
|
case "unlink": {
|
|
32639
|
-
const name = explicitProfile
|
|
32835
|
+
const name = await pickLinkedProfile(explicitProfile, "unlink");
|
|
32836
|
+
if (!name) {
|
|
32837
|
+
process.exitCode = 1;
|
|
32838
|
+
break;
|
|
32839
|
+
}
|
|
32640
32840
|
runUnlink(profileDir(name), name);
|
|
32641
32841
|
break;
|
|
32642
32842
|
}
|
|
@@ -32659,13 +32859,15 @@ async function main() {
|
|
|
32659
32859
|
"4pm \u2014 the 4PM worker-machine CLI",
|
|
32660
32860
|
"",
|
|
32661
32861
|
"Usage:",
|
|
32662
|
-
" 4pm
|
|
32663
|
-
" 4pm
|
|
32664
|
-
" 4pm
|
|
32665
|
-
" 4pm version
|
|
32666
|
-
" 4pm update
|
|
32862
|
+
" 4pm link Pair with the server (hashcode 1\u21922\u21923)",
|
|
32863
|
+
" 4pm start Connect WS, receive commands from the server",
|
|
32864
|
+
" 4pm unlink Delete a link (pick a profile if several)",
|
|
32865
|
+
" 4pm version Show the installed version (+ latest from the server)",
|
|
32866
|
+
" 4pm update Update to the latest version now",
|
|
32667
32867
|
"",
|
|
32668
|
-
"
|
|
32868
|
+
"Profiles: named after the paired account (e.g. mcacc1); when several exist,",
|
|
32869
|
+
"start/unlink show a picker (\u2191\u2193 + Enter) \u2014 no --profile needed.",
|
|
32870
|
+
"Server URL: from FOURPM_SERVER (default http://localhost:3001)."
|
|
32669
32871
|
].join("\n")
|
|
32670
32872
|
);
|
|
32671
32873
|
}
|