@episoda/cli 0.2.163 → 0.2.164
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/daemon/daemon-process.js +218 -38
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +280 -66
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -343,6 +343,8 @@ var require_git_executor = __commonJS({
|
|
|
343
343
|
case "delete_branch":
|
|
344
344
|
return await this.executeDeleteBranch(command, cwd, options);
|
|
345
345
|
// EP597: Read operations for production local dev mode
|
|
346
|
+
case "list_branches":
|
|
347
|
+
return await this.executeListBranches(cwd, options);
|
|
346
348
|
case "branch_exists":
|
|
347
349
|
return await this.executeBranchExists(command, cwd, options);
|
|
348
350
|
case "branch_has_commits":
|
|
@@ -632,6 +634,65 @@ var require_git_executor = __commonJS({
|
|
|
632
634
|
args.push(command.branch);
|
|
633
635
|
return await this.runGitCommand(args, cwd, options);
|
|
634
636
|
}
|
|
637
|
+
/**
|
|
638
|
+
* List local branches with remote-tracking signal and current branch marker.
|
|
639
|
+
*/
|
|
640
|
+
async executeListBranches(cwd, options) {
|
|
641
|
+
try {
|
|
642
|
+
const timeout = options?.timeout || 1e4;
|
|
643
|
+
const branchMap = /* @__PURE__ */ new Map();
|
|
644
|
+
const addBranch = (name, attrs) => {
|
|
645
|
+
const normalized = name.trim();
|
|
646
|
+
if (!normalized)
|
|
647
|
+
return;
|
|
648
|
+
const existing = branchMap.get(normalized);
|
|
649
|
+
if (existing) {
|
|
650
|
+
existing.current = existing.current || !!attrs?.current;
|
|
651
|
+
existing.remote = existing.remote || !!attrs?.remote;
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
branchMap.set(normalized, {
|
|
655
|
+
name: normalized,
|
|
656
|
+
current: !!attrs?.current,
|
|
657
|
+
remote: !!attrs?.remote
|
|
658
|
+
});
|
|
659
|
+
};
|
|
660
|
+
const statusResult = await this.executeStatus(cwd, options);
|
|
661
|
+
const currentBranch = statusResult.success ? statusResult.details?.branchName : void 0;
|
|
662
|
+
try {
|
|
663
|
+
const { stdout } = await execAsync3(`git for-each-ref --format='%(refname:short)' refs/heads`, { cwd, timeout });
|
|
664
|
+
stdout.split("\n").map((line) => line.trim()).filter(Boolean).forEach((name) => addBranch(name, { current: name === currentBranch }));
|
|
665
|
+
} catch {
|
|
666
|
+
}
|
|
667
|
+
try {
|
|
668
|
+
const { stdout } = await execAsync3(`git for-each-ref --format='%(refname:short)' refs/remotes/origin`, { cwd, timeout });
|
|
669
|
+
stdout.split("\n").map((line) => line.trim()).filter(Boolean).forEach((refName) => {
|
|
670
|
+
if (refName === "origin/HEAD")
|
|
671
|
+
return;
|
|
672
|
+
const normalized = refName.startsWith("origin/") ? refName.slice("origin/".length) : refName;
|
|
673
|
+
addBranch(normalized, { remote: true, current: normalized === currentBranch });
|
|
674
|
+
});
|
|
675
|
+
} catch {
|
|
676
|
+
}
|
|
677
|
+
if (currentBranch && currentBranch !== "HEAD") {
|
|
678
|
+
addBranch(currentBranch, { current: true });
|
|
679
|
+
}
|
|
680
|
+
const branches = Array.from(branchMap.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
681
|
+
return {
|
|
682
|
+
success: true,
|
|
683
|
+
details: {
|
|
684
|
+
branchName: currentBranch,
|
|
685
|
+
branches
|
|
686
|
+
}
|
|
687
|
+
};
|
|
688
|
+
} catch (error) {
|
|
689
|
+
return {
|
|
690
|
+
success: false,
|
|
691
|
+
error: "UNKNOWN_ERROR",
|
|
692
|
+
output: error.message || "Failed to list branches"
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
635
696
|
/**
|
|
636
697
|
* EP597: Execute branch_exists command
|
|
637
698
|
* Checks if a branch exists locally and/or remotely
|
|
@@ -2153,6 +2214,8 @@ var require_websocket_client = __commonJS({
|
|
|
2153
2214
|
this.osArch = deviceInfo?.osArch;
|
|
2154
2215
|
this.daemonPid = deviceInfo?.daemonPid;
|
|
2155
2216
|
this.cliVersion = deviceInfo?.cliVersion;
|
|
2217
|
+
this.cliPackageName = deviceInfo?.cliPackageName;
|
|
2218
|
+
this.capabilities = deviceInfo?.capabilities;
|
|
2156
2219
|
this.environment = deviceInfo?.environment;
|
|
2157
2220
|
this.containerId = deviceInfo?.containerId;
|
|
2158
2221
|
this.isDisconnecting = false;
|
|
@@ -2192,6 +2255,8 @@ var require_websocket_client = __commonJS({
|
|
|
2192
2255
|
type: "auth",
|
|
2193
2256
|
token,
|
|
2194
2257
|
version: this.cliVersion || version_1.VERSION,
|
|
2258
|
+
cliPackageName: this.cliPackageName,
|
|
2259
|
+
capabilities: this.capabilities,
|
|
2195
2260
|
environment: this.environment,
|
|
2196
2261
|
machineId,
|
|
2197
2262
|
containerId: this.containerId,
|
|
@@ -2511,6 +2576,8 @@ var require_websocket_client = __commonJS({
|
|
|
2511
2576
|
osArch: this.osArch,
|
|
2512
2577
|
daemonPid: this.daemonPid,
|
|
2513
2578
|
cliVersion: this.cliVersion,
|
|
2579
|
+
cliPackageName: this.cliPackageName,
|
|
2580
|
+
capabilities: this.capabilities,
|
|
2514
2581
|
environment: this.environment,
|
|
2515
2582
|
containerId: this.containerId
|
|
2516
2583
|
}).then(() => {
|
|
@@ -2911,7 +2978,7 @@ var require_package = __commonJS({
|
|
|
2911
2978
|
"package.json"(exports2, module2) {
|
|
2912
2979
|
module2.exports = {
|
|
2913
2980
|
name: "@episoda/cli",
|
|
2914
|
-
version: "0.2.
|
|
2981
|
+
version: "0.2.164",
|
|
2915
2982
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2916
2983
|
main: "dist/index.js",
|
|
2917
2984
|
types: "dist/index.d.ts",
|
|
@@ -7527,7 +7594,7 @@ ${message}`;
|
|
|
7527
7594
|
const args2 = parts.slice(1);
|
|
7528
7595
|
let env;
|
|
7529
7596
|
if (server.name === "github") {
|
|
7530
|
-
env =
|
|
7597
|
+
env = void 0;
|
|
7531
7598
|
} else {
|
|
7532
7599
|
env = {
|
|
7533
7600
|
...baseEnv,
|
|
@@ -8620,8 +8687,8 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8620
8687
|
const allWorktrees = this.listWorktrees();
|
|
8621
8688
|
const activeSet = new Set(activeModuleUids);
|
|
8622
8689
|
const orphaned = allWorktrees.filter((w) => !activeSet.has(w.moduleUid));
|
|
8623
|
-
const
|
|
8624
|
-
return { orphaned, valid };
|
|
8690
|
+
const valid3 = allWorktrees.filter((w) => activeSet.has(w.moduleUid));
|
|
8691
|
+
return { orphaned, valid: valid3 };
|
|
8625
8692
|
}
|
|
8626
8693
|
/**
|
|
8627
8694
|
* Update last accessed timestamp for a worktree
|
|
@@ -8656,7 +8723,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8656
8723
|
*/
|
|
8657
8724
|
async validateWorktrees() {
|
|
8658
8725
|
const config = this.readConfig();
|
|
8659
|
-
const
|
|
8726
|
+
const valid3 = [];
|
|
8660
8727
|
const stale = [];
|
|
8661
8728
|
const orphaned = [];
|
|
8662
8729
|
const listResult = await this.gitExecutor.execute({
|
|
@@ -8667,7 +8734,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8667
8734
|
);
|
|
8668
8735
|
for (const worktree of config?.worktrees || []) {
|
|
8669
8736
|
if (actualWorktrees.has(worktree.worktreePath)) {
|
|
8670
|
-
|
|
8737
|
+
valid3.push(worktree);
|
|
8671
8738
|
actualWorktrees.delete(worktree.worktreePath);
|
|
8672
8739
|
} else {
|
|
8673
8740
|
stale.push(worktree);
|
|
@@ -8678,7 +8745,7 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
8678
8745
|
orphaned.push(wpath);
|
|
8679
8746
|
}
|
|
8680
8747
|
}
|
|
8681
|
-
return { valid, stale, orphaned };
|
|
8748
|
+
return { valid: valid3, stale, orphaned };
|
|
8682
8749
|
}
|
|
8683
8750
|
/**
|
|
8684
8751
|
* EP1190: Clean up non-module worktrees (like 'main')
|
|
@@ -11551,6 +11618,18 @@ async function deleteWorktree(config, moduleUid) {
|
|
|
11551
11618
|
// src/daemon/package-manager.ts
|
|
11552
11619
|
var fs21 = __toESM(require("fs"));
|
|
11553
11620
|
var path22 = __toESM(require("path"));
|
|
11621
|
+
function pnpmCommand(args) {
|
|
11622
|
+
return [
|
|
11623
|
+
"if command -v pnpm >/dev/null 2>&1; then",
|
|
11624
|
+
` pnpm ${args}`,
|
|
11625
|
+
"elif command -v corepack >/dev/null 2>&1; then",
|
|
11626
|
+
` corepack pnpm ${args}`,
|
|
11627
|
+
"else",
|
|
11628
|
+
' echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2',
|
|
11629
|
+
" exit 127",
|
|
11630
|
+
"fi"
|
|
11631
|
+
].join(" ");
|
|
11632
|
+
}
|
|
11554
11633
|
var PACKAGE_MANAGERS = {
|
|
11555
11634
|
javascript: [
|
|
11556
11635
|
{
|
|
@@ -11572,7 +11651,7 @@ var PACKAGE_MANAGERS = {
|
|
|
11572
11651
|
return null;
|
|
11573
11652
|
},
|
|
11574
11653
|
config: {
|
|
11575
|
-
installCmd: "
|
|
11654
|
+
installCmd: pnpmCommand("install --frozen-lockfile"),
|
|
11576
11655
|
cacheEnvVar: "PNPM_HOME"
|
|
11577
11656
|
}
|
|
11578
11657
|
},
|
|
@@ -11604,7 +11683,7 @@ var PACKAGE_MANAGERS = {
|
|
|
11604
11683
|
name: "pnpm",
|
|
11605
11684
|
detector: (p) => fs21.existsSync(path22.join(p, "package.json")) ? "package.json (no lockfile - defaulting to pnpm)" : null,
|
|
11606
11685
|
config: {
|
|
11607
|
-
installCmd: "
|
|
11686
|
+
installCmd: pnpmCommand("install"),
|
|
11608
11687
|
cacheEnvVar: "PNPM_HOME"
|
|
11609
11688
|
}
|
|
11610
11689
|
}
|
|
@@ -11752,7 +11831,7 @@ async function autoDetectSetupScript(worktreePath) {
|
|
|
11752
11831
|
function getBuildPackagesCommand(packageManagerName) {
|
|
11753
11832
|
switch (packageManagerName) {
|
|
11754
11833
|
case "pnpm":
|
|
11755
|
-
return
|
|
11834
|
+
return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
|
|
11756
11835
|
case "yarn":
|
|
11757
11836
|
return "yarn build:packages";
|
|
11758
11837
|
case "npm":
|
|
@@ -12771,6 +12850,7 @@ var fs25 = __toESM(require("fs"));
|
|
|
12771
12850
|
var path26 = __toESM(require("path"));
|
|
12772
12851
|
var import_child_process18 = require("child_process");
|
|
12773
12852
|
var import_core17 = __toESM(require_dist());
|
|
12853
|
+
var semver2 = __toESM(require("semver"));
|
|
12774
12854
|
|
|
12775
12855
|
// src/utils/update-checker.ts
|
|
12776
12856
|
var import_child_process17 = require("child_process");
|
|
@@ -12781,6 +12861,7 @@ var import_core16 = __toESM(require_dist());
|
|
|
12781
12861
|
|
|
12782
12862
|
// src/utils/update-checker.ts
|
|
12783
12863
|
var PACKAGE_NAME = "@episoda/cli";
|
|
12864
|
+
var LEGACY_PACKAGE_NAME = "episoda";
|
|
12784
12865
|
var NPM_REGISTRY = "https://registry.npmjs.org";
|
|
12785
12866
|
function isFileLinkedInstall() {
|
|
12786
12867
|
try {
|
|
@@ -12853,6 +12934,39 @@ function getInstalledVersion() {
|
|
|
12853
12934
|
return null;
|
|
12854
12935
|
}
|
|
12855
12936
|
}
|
|
12937
|
+
function getLegacyInstalledVersion() {
|
|
12938
|
+
try {
|
|
12939
|
+
const output = (0, import_child_process17.execSync)(`npm list -g ${LEGACY_PACKAGE_NAME} --json`, {
|
|
12940
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
12941
|
+
timeout: 1e4
|
|
12942
|
+
}).toString();
|
|
12943
|
+
const data = JSON.parse(output);
|
|
12944
|
+
return data?.dependencies?.[LEGACY_PACKAGE_NAME]?.version || null;
|
|
12945
|
+
} catch {
|
|
12946
|
+
return null;
|
|
12947
|
+
}
|
|
12948
|
+
}
|
|
12949
|
+
function detectCliInstallChannel(embeddedVersion) {
|
|
12950
|
+
const scopedVersion = getInstalledVersion();
|
|
12951
|
+
const legacyVersion = getLegacyInstalledVersion();
|
|
12952
|
+
const effectiveVersion = scopedVersion || legacyVersion || embeddedVersion || null;
|
|
12953
|
+
return {
|
|
12954
|
+
scopedVersion,
|
|
12955
|
+
legacyVersion,
|
|
12956
|
+
legacyOnly: !scopedVersion && !!legacyVersion,
|
|
12957
|
+
effectiveVersion
|
|
12958
|
+
};
|
|
12959
|
+
}
|
|
12960
|
+
function resolveEffectiveCliVersion(embeddedVersion) {
|
|
12961
|
+
const installedVersion = detectCliInstallChannel(embeddedVersion).effectiveVersion;
|
|
12962
|
+
if (!installedVersion) {
|
|
12963
|
+
return embeddedVersion;
|
|
12964
|
+
}
|
|
12965
|
+
if (semver.valid(installedVersion) && semver.valid(embeddedVersion)) {
|
|
12966
|
+
return semver.gt(installedVersion, embeddedVersion) ? installedVersion : embeddedVersion;
|
|
12967
|
+
}
|
|
12968
|
+
return installedVersion === embeddedVersion ? embeddedVersion : installedVersion;
|
|
12969
|
+
}
|
|
12856
12970
|
|
|
12857
12971
|
// src/daemon/update-manager.ts
|
|
12858
12972
|
var UpdateManager = class _UpdateManager {
|
|
@@ -12875,6 +12989,19 @@ var UpdateManager = class _UpdateManager {
|
|
|
12875
12989
|
// 4 hours
|
|
12876
12990
|
this.MAX_UPDATE_ATTEMPTS = 3;
|
|
12877
12991
|
}
|
|
12992
|
+
/**
|
|
12993
|
+
* Prefer installed CLI version when it's newer than embedded bundle metadata.
|
|
12994
|
+
* This avoids update churn when daemon bundle version lags package install version.
|
|
12995
|
+
*/
|
|
12996
|
+
getEffectiveCurrentVersion() {
|
|
12997
|
+
return resolveEffectiveCliVersion(this.currentVersion);
|
|
12998
|
+
}
|
|
12999
|
+
isTargetVersionNewer(targetVersion, currentVersion) {
|
|
13000
|
+
if (semver2.valid(targetVersion) && semver2.valid(currentVersion)) {
|
|
13001
|
+
return semver2.gt(targetVersion, currentVersion);
|
|
13002
|
+
}
|
|
13003
|
+
return targetVersion !== currentVersion;
|
|
13004
|
+
}
|
|
12878
13005
|
/**
|
|
12879
13006
|
* Start periodic update checks (every 4 hours).
|
|
12880
13007
|
* Does nothing if EPISODA_CLI_PIN_VERSION is set.
|
|
@@ -12919,7 +13046,8 @@ var UpdateManager = class _UpdateManager {
|
|
|
12919
13046
|
return;
|
|
12920
13047
|
}
|
|
12921
13048
|
try {
|
|
12922
|
-
const
|
|
13049
|
+
const effectiveCurrentVersion = this.getEffectiveCurrentVersion();
|
|
13050
|
+
const result = await checkForUpdates(effectiveCurrentVersion);
|
|
12923
13051
|
if (result.updateAvailable) {
|
|
12924
13052
|
await this.applyUpdateIfIdle(result.latestVersion, "startup");
|
|
12925
13053
|
}
|
|
@@ -12941,8 +13069,9 @@ var UpdateManager = class _UpdateManager {
|
|
|
12941
13069
|
}
|
|
12942
13070
|
async applyUpdateIfIdle(targetVersion, source) {
|
|
12943
13071
|
if (this.updateInProgress) return;
|
|
12944
|
-
|
|
12945
|
-
|
|
13072
|
+
const effectiveCurrentVersion = this.getEffectiveCurrentVersion();
|
|
13073
|
+
if (!this.isTargetVersionNewer(targetVersion, effectiveCurrentVersion)) {
|
|
13074
|
+
console.log(`[Daemon] EP1390: Skipping update target=${targetVersion}, current=${effectiveCurrentVersion} (source=${source})`);
|
|
12946
13075
|
return;
|
|
12947
13076
|
}
|
|
12948
13077
|
if (this.lastFailedUpdateVersion === targetVersion && this.updateFailedAttempts >= _UpdateManager.MAX_UPDATE_ATTEMPTS) {
|
|
@@ -12952,12 +13081,12 @@ var UpdateManager = class _UpdateManager {
|
|
|
12952
13081
|
const activeAgentSessions = this.getActiveAgentSessionCount();
|
|
12953
13082
|
if (activeAgentSessions > 0) {
|
|
12954
13083
|
this.pendingUpdateVersion = targetVersion;
|
|
12955
|
-
console.log(`[Daemon] EP1319: Update available (${
|
|
13084
|
+
console.log(`[Daemon] EP1319: Update available (${effectiveCurrentVersion} \u2192 ${targetVersion}) but ${activeAgentSessions} active agent session(s) \u2014 deferring`);
|
|
12956
13085
|
return;
|
|
12957
13086
|
}
|
|
12958
|
-
const
|
|
12959
|
-
if (
|
|
12960
|
-
console.log(`[Daemon]
|
|
13087
|
+
const latestEffectiveCurrent = this.getEffectiveCurrentVersion();
|
|
13088
|
+
if (!this.isTargetVersionNewer(targetVersion, latestEffectiveCurrent)) {
|
|
13089
|
+
console.log(`[Daemon] EP1390: Update no longer needed target=${targetVersion}, current=${latestEffectiveCurrent}`);
|
|
12961
13090
|
return;
|
|
12962
13091
|
}
|
|
12963
13092
|
this.updateInProgress = true;
|
|
@@ -12983,6 +13112,14 @@ var UpdateManager = class _UpdateManager {
|
|
|
12983
13112
|
stdio: ["ignore", logFd, logFd],
|
|
12984
13113
|
env: { ...process.env, EPISODA_DAEMON_MODE: "1" }
|
|
12985
13114
|
});
|
|
13115
|
+
if (!child.pid) {
|
|
13116
|
+
try {
|
|
13117
|
+
fs25.closeSync(logFd);
|
|
13118
|
+
} catch {
|
|
13119
|
+
}
|
|
13120
|
+
this.recordUpdateFailure(targetVersion, "Failed to spawn replacement daemon (missing pid)");
|
|
13121
|
+
return;
|
|
13122
|
+
}
|
|
12986
13123
|
child.unref();
|
|
12987
13124
|
const pidPath = getPidFilePath();
|
|
12988
13125
|
fs25.writeFileSync(pidPath, child.pid.toString(), "utf-8");
|
|
@@ -13019,7 +13156,8 @@ var UpdateManager = class _UpdateManager {
|
|
|
13019
13156
|
return;
|
|
13020
13157
|
}
|
|
13021
13158
|
try {
|
|
13022
|
-
const
|
|
13159
|
+
const effectiveCurrentVersion = this.getEffectiveCurrentVersion();
|
|
13160
|
+
const result = await checkForUpdates(effectiveCurrentVersion);
|
|
13023
13161
|
if (!result.updateAvailable) {
|
|
13024
13162
|
if (this.pendingUpdateVersion) {
|
|
13025
13163
|
console.log(`[Daemon] EP1319: Pending update to ${this.pendingUpdateVersion} is no longer needed (already current)`);
|
|
@@ -13679,6 +13817,52 @@ var ConnectionManager = class {
|
|
|
13679
13817
|
}
|
|
13680
13818
|
};
|
|
13681
13819
|
|
|
13820
|
+
// src/daemon/ws-endpoint.ts
|
|
13821
|
+
var DEFAULT_API_URL = "https://episoda.dev";
|
|
13822
|
+
var CANONICAL_PROD_WS_HOST = "ws.episoda.dev";
|
|
13823
|
+
var PRODUCTION_API_HOSTS = /* @__PURE__ */ new Set([
|
|
13824
|
+
"episoda.dev",
|
|
13825
|
+
"www.episoda.dev",
|
|
13826
|
+
"api.episoda.dev"
|
|
13827
|
+
]);
|
|
13828
|
+
function parseApiUrl(rawUrl) {
|
|
13829
|
+
try {
|
|
13830
|
+
return new URL(rawUrl);
|
|
13831
|
+
} catch {
|
|
13832
|
+
return new URL(DEFAULT_API_URL);
|
|
13833
|
+
}
|
|
13834
|
+
}
|
|
13835
|
+
function resolveDaemonWsEndpoint(config, env = process.env) {
|
|
13836
|
+
if (config.ws_url) {
|
|
13837
|
+
return {
|
|
13838
|
+
wsUrl: config.ws_url,
|
|
13839
|
+
source: "config.ws_url",
|
|
13840
|
+
serverUrl: config.project_settings?.local_server_url || config.api_url || env.EPISODA_API_URL || DEFAULT_API_URL
|
|
13841
|
+
};
|
|
13842
|
+
}
|
|
13843
|
+
if (env.EPISODA_WS_URL) {
|
|
13844
|
+
return {
|
|
13845
|
+
wsUrl: env.EPISODA_WS_URL,
|
|
13846
|
+
source: "env.EPISODA_WS_URL",
|
|
13847
|
+
serverUrl: config.project_settings?.local_server_url || config.api_url || env.EPISODA_API_URL || DEFAULT_API_URL
|
|
13848
|
+
};
|
|
13849
|
+
}
|
|
13850
|
+
const rawServerUrl = config.project_settings?.local_server_url || config.api_url || env.EPISODA_API_URL || DEFAULT_API_URL;
|
|
13851
|
+
const serverUrl = parseApiUrl(rawServerUrl);
|
|
13852
|
+
const wsProtocol = serverUrl.protocol === "https:" ? "wss:" : "ws:";
|
|
13853
|
+
const useCanonicalProdHost = PRODUCTION_API_HOSTS.has(serverUrl.hostname);
|
|
13854
|
+
const wsHostname = useCanonicalProdHost ? CANONICAL_PROD_WS_HOST : serverUrl.hostname;
|
|
13855
|
+
const explicitPort = env.EPISODA_WS_PORT?.trim();
|
|
13856
|
+
const inheritedServerPort = !useCanonicalProdHost ? serverUrl.port : "";
|
|
13857
|
+
const wsPort = explicitPort || inheritedServerPort;
|
|
13858
|
+
const wsUrl = wsPort ? `${wsProtocol}//${wsHostname}:${wsPort}` : `${wsProtocol}//${wsHostname}`;
|
|
13859
|
+
return {
|
|
13860
|
+
wsUrl,
|
|
13861
|
+
source: "derived",
|
|
13862
|
+
serverUrl: serverUrl.toString()
|
|
13863
|
+
};
|
|
13864
|
+
}
|
|
13865
|
+
|
|
13682
13866
|
// src/daemon/project-message-router.ts
|
|
13683
13867
|
var import_core19 = __toESM(require_dist());
|
|
13684
13868
|
var path28 = __toESM(require("path"));
|
|
@@ -14182,7 +14366,7 @@ function getBuildPackagesCommand2(installCmd) {
|
|
|
14182
14366
|
const runner = installCmd?.command?.[0];
|
|
14183
14367
|
switch (runner) {
|
|
14184
14368
|
case "pnpm":
|
|
14185
|
-
return
|
|
14369
|
+
return 'if command -v pnpm >/dev/null 2>&1; then pnpm run build:packages; elif command -v corepack >/dev/null 2>&1; then corepack pnpm run build:packages; else echo "[setup] ERROR: pnpm is not installed and corepack is unavailable" >&2; exit 127; fi';
|
|
14186
14370
|
case "yarn":
|
|
14187
14371
|
return "yarn build:packages";
|
|
14188
14372
|
case "npm":
|
|
@@ -14217,10 +14401,11 @@ var Daemon = class _Daemon {
|
|
|
14217
14401
|
// 2 minutes
|
|
14218
14402
|
// EP1360: Per-session monotonic event seq for daemon→platform stream gap detection.
|
|
14219
14403
|
this.agentEventSeq = /* @__PURE__ */ new Map();
|
|
14404
|
+
this.cliRuntimeVersion = resolveEffectiveCliVersion(packageJson.version);
|
|
14220
14405
|
this.ipcServer = new IPCServer();
|
|
14221
14406
|
this.connectionManager = new ConnectionManager();
|
|
14222
14407
|
this.ipcRouter = new IPCRouter(this, this.ipcServer);
|
|
14223
|
-
this.updateManager = new UpdateManager(this,
|
|
14408
|
+
this.updateManager = new UpdateManager(this, this.cliRuntimeVersion, __filename);
|
|
14224
14409
|
this.healthOrchestrator = new HealthOrchestrator(this);
|
|
14225
14410
|
this.daemonCore = new DaemonCore(this);
|
|
14226
14411
|
this.projectMessageRouter = new ProjectMessageRouter({
|
|
@@ -14485,23 +14670,15 @@ var Daemon = class _Daemon {
|
|
|
14485
14670
|
if (!config || !config.access_token) {
|
|
14486
14671
|
throw new Error("No access token found. Please run: episoda auth");
|
|
14487
14672
|
}
|
|
14488
|
-
|
|
14489
|
-
if (config.
|
|
14490
|
-
|
|
14491
|
-
|
|
14492
|
-
|
|
14493
|
-
|
|
14494
|
-
|
|
14495
|
-
wsUrl = config.ws_url;
|
|
14496
|
-
console.log(`[Daemon] Using configured ws_url: ${wsUrl}`);
|
|
14497
|
-
} else {
|
|
14498
|
-
const serverUrlObj = new URL(serverUrl);
|
|
14499
|
-
const wsProtocol = serverUrlObj.protocol === "https:" ? "wss:" : "ws:";
|
|
14500
|
-
const wsHostname = serverUrlObj.hostname === "episoda.dev" ? "ws.episoda.dev" : serverUrlObj.hostname;
|
|
14501
|
-
const wsPort = process.env.EPISODA_WS_PORT;
|
|
14502
|
-
wsUrl = wsPort ? `${wsProtocol}//${wsHostname}:${wsPort}` : `${wsProtocol}//${wsHostname}`;
|
|
14673
|
+
const wsEndpoint = resolveDaemonWsEndpoint(config);
|
|
14674
|
+
if (wsEndpoint.source === "config.ws_url") {
|
|
14675
|
+
console.log(`[Daemon] Using configured ws_url: ${wsEndpoint.wsUrl}`);
|
|
14676
|
+
} else if (wsEndpoint.source === "env.EPISODA_WS_URL") {
|
|
14677
|
+
console.log(`[Daemon] Using EPISODA_WS_URL override: ${wsEndpoint.wsUrl}`);
|
|
14678
|
+
} else if (config.project_settings?.local_server_url) {
|
|
14679
|
+
console.log(`[Daemon] Using cached server URL: ${wsEndpoint.serverUrl}`);
|
|
14503
14680
|
}
|
|
14504
|
-
console.log(`[Daemon] Connecting to ${wsUrl} for project ${projectId}...`);
|
|
14681
|
+
console.log(`[Daemon] Connecting to ${wsEndpoint.wsUrl} for project ${projectId} (source: ${wsEndpoint.source})...`);
|
|
14505
14682
|
const client = new import_core22.EpisodaClient();
|
|
14506
14683
|
const gitExecutor = new import_core22.GitExecutor();
|
|
14507
14684
|
const connection = {
|
|
@@ -14995,12 +15172,15 @@ var Daemon = class _Daemon {
|
|
|
14995
15172
|
const modeConfig = getDaemonModeConfig();
|
|
14996
15173
|
const environment = modeConfig.mode;
|
|
14997
15174
|
const containerId = process.env.EPISODA_CONTAINER_ID;
|
|
14998
|
-
|
|
15175
|
+
const capabilities = ["worktree_create_v1"];
|
|
15176
|
+
await client.connect(wsEndpoint.wsUrl, config.access_token, this.machineId, {
|
|
14999
15177
|
hostname: os12.hostname(),
|
|
15000
15178
|
osPlatform: os12.platform(),
|
|
15001
15179
|
osArch: os12.arch(),
|
|
15002
15180
|
daemonPid,
|
|
15003
|
-
cliVersion:
|
|
15181
|
+
cliVersion: this.cliRuntimeVersion,
|
|
15182
|
+
cliPackageName: packageJson.name,
|
|
15183
|
+
capabilities,
|
|
15004
15184
|
environment,
|
|
15005
15185
|
containerId
|
|
15006
15186
|
});
|