episoda 0.2.62 → 0.2.64
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 +96 -45
- package/dist/daemon/daemon-process.js.map +1 -1
- package/dist/index.js +51 -6
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -2726,7 +2726,7 @@ var require_package = __commonJS({
|
|
|
2726
2726
|
"package.json"(exports2, module2) {
|
|
2727
2727
|
module2.exports = {
|
|
2728
2728
|
name: "episoda",
|
|
2729
|
-
version: "0.2.
|
|
2729
|
+
version: "0.2.64",
|
|
2730
2730
|
description: "CLI tool for Episoda local development workflow orchestration",
|
|
2731
2731
|
main: "dist/index.js",
|
|
2732
2732
|
types: "dist/index.d.ts",
|
|
@@ -7091,23 +7091,40 @@ var WorktreeManager = class _WorktreeManager {
|
|
|
7091
7091
|
/**
|
|
7092
7092
|
* Initialize worktree manager from existing project root
|
|
7093
7093
|
* EP971: All projects use worktree architecture
|
|
7094
|
+
* EP1093: Added debug logging for cloud container diagnostics
|
|
7094
7095
|
* @returns true if valid project, false otherwise
|
|
7095
7096
|
*/
|
|
7096
7097
|
async initialize() {
|
|
7098
|
+
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
7097
7099
|
if (!fs15.existsSync(this.bareRepoPath)) {
|
|
7100
|
+
if (debug) {
|
|
7101
|
+
console.log(`[WorktreeManager] initialize: .bare not found at ${this.bareRepoPath}`);
|
|
7102
|
+
}
|
|
7098
7103
|
return false;
|
|
7099
7104
|
}
|
|
7100
7105
|
if (!fs15.existsSync(this.configPath)) {
|
|
7106
|
+
if (debug) {
|
|
7107
|
+
console.log(`[WorktreeManager] initialize: config not found at ${this.configPath}`);
|
|
7108
|
+
}
|
|
7101
7109
|
return false;
|
|
7102
7110
|
}
|
|
7103
7111
|
try {
|
|
7104
7112
|
const config = this.readConfig();
|
|
7105
7113
|
if (config === null) {
|
|
7114
|
+
if (debug) {
|
|
7115
|
+
console.log(`[WorktreeManager] initialize: readConfig returned null`);
|
|
7116
|
+
}
|
|
7106
7117
|
return false;
|
|
7107
7118
|
}
|
|
7119
|
+
if (debug) {
|
|
7120
|
+
console.log(`[WorktreeManager] initialize: config loaded, projectId=${config.projectId}`);
|
|
7121
|
+
}
|
|
7108
7122
|
await this.ensureFetchRefspecConfigured();
|
|
7109
7123
|
return true;
|
|
7110
|
-
} catch {
|
|
7124
|
+
} catch (error) {
|
|
7125
|
+
if (debug) {
|
|
7126
|
+
console.log(`[WorktreeManager] initialize: exception - ${error.message}`);
|
|
7127
|
+
}
|
|
7111
7128
|
return false;
|
|
7112
7129
|
}
|
|
7113
7130
|
}
|
|
@@ -7719,20 +7736,43 @@ function getEpisodaRoot() {
|
|
|
7719
7736
|
return process.env.EPISODA_ROOT || path16.join(require("os").homedir(), "episoda");
|
|
7720
7737
|
}
|
|
7721
7738
|
async function isWorktreeProject(projectRoot) {
|
|
7739
|
+
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
7740
|
+
if (debug) {
|
|
7741
|
+
console.log(`[WorktreeManager] isWorktreeProject: checking ${projectRoot}`);
|
|
7742
|
+
}
|
|
7722
7743
|
const manager = new WorktreeManager(projectRoot);
|
|
7723
|
-
|
|
7744
|
+
const result = await manager.initialize();
|
|
7745
|
+
if (debug) {
|
|
7746
|
+
console.log(`[WorktreeManager] isWorktreeProject: ${projectRoot} -> ${result}`);
|
|
7747
|
+
}
|
|
7748
|
+
return result;
|
|
7724
7749
|
}
|
|
7725
7750
|
async function findProjectRoot(startPath) {
|
|
7726
7751
|
let current = path16.resolve(startPath);
|
|
7727
7752
|
const episodaRoot = getEpisodaRoot();
|
|
7753
|
+
const debug = process.env.EPISODA_DEBUG === "1" || process.env.GIT_CREDENTIAL_EPISODA_DEBUG === "1";
|
|
7754
|
+
if (debug) {
|
|
7755
|
+
console.log(`[WorktreeManager] findProjectRoot: start=${startPath}, episodaRoot=${episodaRoot}`);
|
|
7756
|
+
}
|
|
7728
7757
|
if (!current.startsWith(episodaRoot)) {
|
|
7758
|
+
if (debug) {
|
|
7759
|
+
console.log(`[WorktreeManager] findProjectRoot: ${current} is not under ${episodaRoot}`);
|
|
7760
|
+
}
|
|
7729
7761
|
return null;
|
|
7730
7762
|
}
|
|
7731
7763
|
for (let i = 0; i < 10; i++) {
|
|
7732
7764
|
const bareDir = path16.join(current, ".bare");
|
|
7733
7765
|
const episodaDir = path16.join(current, ".episoda");
|
|
7766
|
+
if (debug) {
|
|
7767
|
+
const bareExists = fs15.existsSync(bareDir);
|
|
7768
|
+
const episodaExists = fs15.existsSync(episodaDir);
|
|
7769
|
+
console.log(`[WorktreeManager] findProjectRoot: checking ${current} (.bare=${bareExists}, .episoda=${episodaExists})`);
|
|
7770
|
+
}
|
|
7734
7771
|
if (fs15.existsSync(bareDir) && fs15.existsSync(episodaDir)) {
|
|
7735
7772
|
if (await isWorktreeProject(current)) {
|
|
7773
|
+
if (debug) {
|
|
7774
|
+
console.log(`[WorktreeManager] findProjectRoot: found valid project at ${current}`);
|
|
7775
|
+
}
|
|
7736
7776
|
return current;
|
|
7737
7777
|
}
|
|
7738
7778
|
}
|
|
@@ -7742,6 +7782,9 @@ async function findProjectRoot(startPath) {
|
|
|
7742
7782
|
}
|
|
7743
7783
|
current = parent;
|
|
7744
7784
|
}
|
|
7785
|
+
if (debug) {
|
|
7786
|
+
console.log(`[WorktreeManager] findProjectRoot: no project found`);
|
|
7787
|
+
}
|
|
7745
7788
|
return null;
|
|
7746
7789
|
}
|
|
7747
7790
|
|
|
@@ -7964,8 +8007,8 @@ var Daemon = class _Daemon {
|
|
|
7964
8007
|
// 60 seconds
|
|
7965
8008
|
constructor() {
|
|
7966
8009
|
this.machineId = "";
|
|
7967
|
-
this.
|
|
7968
|
-
//
|
|
8010
|
+
this.machineUuid = null;
|
|
8011
|
+
// EP1091: Renamed from deviceId for platform terminology alignment
|
|
7969
8012
|
this.deviceName = null;
|
|
7970
8013
|
// EP661: Cached device name from server
|
|
7971
8014
|
this.flyMachineId = null;
|
|
@@ -8024,9 +8067,9 @@ var Daemon = class _Daemon {
|
|
|
8024
8067
|
this.machineId = await getMachineId();
|
|
8025
8068
|
console.log(`[Daemon] Machine ID: ${this.machineId}`);
|
|
8026
8069
|
const config = await (0, import_core12.loadConfig)();
|
|
8027
|
-
if (config?.device_id) {
|
|
8028
|
-
this.
|
|
8029
|
-
console.log(`[Daemon] Loaded cached
|
|
8070
|
+
if (config?.machine_uuid || config?.device_id) {
|
|
8071
|
+
this.machineUuid = config.machine_uuid || config.device_id || null;
|
|
8072
|
+
console.log(`[Daemon] Loaded cached Machine UUID: ${this.machineUuid}`);
|
|
8030
8073
|
}
|
|
8031
8074
|
await this.ipcServer.start();
|
|
8032
8075
|
console.log("[Daemon] IPC server started");
|
|
@@ -8078,8 +8121,10 @@ var Daemon = class _Daemon {
|
|
|
8078
8121
|
return {
|
|
8079
8122
|
running: true,
|
|
8080
8123
|
machineId: this.machineId,
|
|
8081
|
-
|
|
8082
|
-
//
|
|
8124
|
+
machineUuid: this.machineUuid,
|
|
8125
|
+
// EP1091: New preferred field name
|
|
8126
|
+
deviceId: this.machineUuid,
|
|
8127
|
+
// EP726: Kept for backward compatibility
|
|
8083
8128
|
hostname: os8.hostname(),
|
|
8084
8129
|
platform: os8.platform(),
|
|
8085
8130
|
arch: os8.arch(),
|
|
@@ -8791,18 +8836,19 @@ var Daemon = class _Daemon {
|
|
|
8791
8836
|
this.liveConnections.add(projectPath);
|
|
8792
8837
|
this.pendingConnections.delete(projectPath);
|
|
8793
8838
|
const authMessage = message;
|
|
8839
|
+
const effectiveMachineUuid = authMessage.machineUuid || authMessage.deviceId;
|
|
8794
8840
|
if (authMessage.userId && authMessage.workspaceId) {
|
|
8795
|
-
await this.configureGitUser(projectPath, authMessage.userId, authMessage.workspaceId, this.machineId, projectId,
|
|
8841
|
+
await this.configureGitUser(projectPath, authMessage.userId, authMessage.workspaceId, this.machineId, projectId, effectiveMachineUuid);
|
|
8796
8842
|
await this.installGitHooks(projectPath);
|
|
8797
8843
|
}
|
|
8798
8844
|
if (authMessage.deviceName) {
|
|
8799
8845
|
this.deviceName = authMessage.deviceName;
|
|
8800
8846
|
console.log(`[Daemon] Device name: ${this.deviceName}`);
|
|
8801
8847
|
}
|
|
8802
|
-
if (
|
|
8803
|
-
this.
|
|
8804
|
-
console.log(`[Daemon]
|
|
8805
|
-
await this.
|
|
8848
|
+
if (effectiveMachineUuid) {
|
|
8849
|
+
this.machineUuid = effectiveMachineUuid;
|
|
8850
|
+
console.log(`[Daemon] Machine UUID: ${this.machineUuid}`);
|
|
8851
|
+
await this.cacheMachineUuid(effectiveMachineUuid);
|
|
8806
8852
|
}
|
|
8807
8853
|
if (authMessage.flyMachineId) {
|
|
8808
8854
|
this.flyMachineId = authMessage.flyMachineId;
|
|
@@ -8824,9 +8870,6 @@ var Daemon = class _Daemon {
|
|
|
8824
8870
|
this.reconcileWorktrees(projectId, projectPath, client).catch((err) => {
|
|
8825
8871
|
console.warn("[Daemon] EP1003: Reconciliation report failed:", err.message);
|
|
8826
8872
|
});
|
|
8827
|
-
this.reconcilePendingCleanups(projectId, projectPath).catch((err) => {
|
|
8828
|
-
console.warn("[Daemon] EP1047: Cleanup queue reconciliation failed:", err.message);
|
|
8829
|
-
});
|
|
8830
8873
|
});
|
|
8831
8874
|
client.on("module_state_changed", async (message) => {
|
|
8832
8875
|
if (message.type === "module_state_changed") {
|
|
@@ -8836,7 +8879,7 @@ var Daemon = class _Daemon {
|
|
|
8836
8879
|
console.log(`[Daemon] EP1003: State change for non-local module ${moduleUid} (mode: ${devMode || "unknown"})`);
|
|
8837
8880
|
return;
|
|
8838
8881
|
}
|
|
8839
|
-
if (checkoutMachineId && checkoutMachineId !== this.
|
|
8882
|
+
if (checkoutMachineId && checkoutMachineId !== this.machineUuid) {
|
|
8840
8883
|
console.log(`[Daemon] EP1003: State change for ${moduleUid} handled by different machine: ${checkoutMachineId}`);
|
|
8841
8884
|
return;
|
|
8842
8885
|
}
|
|
@@ -8941,12 +8984,13 @@ var Daemon = class _Daemon {
|
|
|
8941
8984
|
* EP595: Configure git with user and workspace ID for post-checkout hook
|
|
8942
8985
|
* EP655: Added machineId for device isolation in multi-device environments
|
|
8943
8986
|
* EP725: Added projectId for main branch badge tracking
|
|
8944
|
-
* EP726: Added
|
|
8987
|
+
* EP726: Added machineUuid (UUID) for unified device identification
|
|
8988
|
+
* EP1091: Renamed deviceId param to machineUuid for consistency
|
|
8945
8989
|
*
|
|
8946
8990
|
* This stores the IDs in .git/config so the post-checkout hook can
|
|
8947
8991
|
* update module.checkout_* fields when git operations happen from terminal.
|
|
8948
8992
|
*/
|
|
8949
|
-
async configureGitUser(projectPath, userId, workspaceId, machineId, projectId,
|
|
8993
|
+
async configureGitUser(projectPath, userId, workspaceId, machineId, projectId, machineUuid) {
|
|
8950
8994
|
try {
|
|
8951
8995
|
const { execSync: execSync9 } = await import("child_process");
|
|
8952
8996
|
execSync9(`git config episoda.userId ${userId}`, {
|
|
@@ -8969,14 +9013,14 @@ var Daemon = class _Daemon {
|
|
|
8969
9013
|
encoding: "utf8",
|
|
8970
9014
|
stdio: "pipe"
|
|
8971
9015
|
});
|
|
8972
|
-
if (
|
|
8973
|
-
execSync9(`git config episoda.deviceId ${
|
|
9016
|
+
if (machineUuid) {
|
|
9017
|
+
execSync9(`git config episoda.deviceId ${machineUuid}`, {
|
|
8974
9018
|
cwd: projectPath,
|
|
8975
9019
|
encoding: "utf8",
|
|
8976
9020
|
stdio: "pipe"
|
|
8977
9021
|
});
|
|
8978
9022
|
}
|
|
8979
|
-
console.log(`[Daemon] Configured git for project: episoda.userId=${userId}, machineId=${machineId}, projectId=${projectId}${
|
|
9023
|
+
console.log(`[Daemon] Configured git for project: episoda.userId=${userId}, machineId=${machineId}, projectId=${projectId}${machineUuid ? `, machineUuid=${machineUuid}` : ""}`);
|
|
8980
9024
|
} catch (error) {
|
|
8981
9025
|
console.warn(`[Daemon] Failed to configure git user for ${projectPath}:`, error instanceof Error ? error.message : error);
|
|
8982
9026
|
}
|
|
@@ -9018,30 +9062,34 @@ var Daemon = class _Daemon {
|
|
|
9018
9062
|
}
|
|
9019
9063
|
}
|
|
9020
9064
|
/**
|
|
9021
|
-
* EP726: Cache
|
|
9065
|
+
* EP726: Cache machine UUID to config file
|
|
9066
|
+
* EP1091: Renamed from cacheDeviceId, writes machine_uuid (new) and device_id (backward compat)
|
|
9022
9067
|
*
|
|
9023
|
-
* Persists the
|
|
9068
|
+
* Persists the machine UUID received from the server so it's available
|
|
9024
9069
|
* on daemon restart without needing to re-register the device.
|
|
9025
9070
|
*/
|
|
9026
|
-
async
|
|
9071
|
+
async cacheMachineUuid(machineUuid) {
|
|
9027
9072
|
try {
|
|
9028
9073
|
const config = await (0, import_core12.loadConfig)();
|
|
9029
9074
|
if (!config) {
|
|
9030
|
-
console.warn("[Daemon] Cannot cache
|
|
9075
|
+
console.warn("[Daemon] Cannot cache machine UUID - no config found");
|
|
9031
9076
|
return;
|
|
9032
9077
|
}
|
|
9033
|
-
if (config.
|
|
9078
|
+
if (config.machine_uuid === machineUuid) {
|
|
9034
9079
|
return;
|
|
9035
9080
|
}
|
|
9036
9081
|
const updatedConfig = {
|
|
9037
9082
|
...config,
|
|
9038
|
-
|
|
9083
|
+
machine_uuid: machineUuid,
|
|
9084
|
+
// EP1091: New preferred field
|
|
9085
|
+
device_id: machineUuid,
|
|
9086
|
+
// Backward compatibility
|
|
9039
9087
|
machine_id: this.machineId
|
|
9040
9088
|
};
|
|
9041
9089
|
await (0, import_core12.saveConfig)(updatedConfig);
|
|
9042
|
-
console.log(`[Daemon] Cached
|
|
9090
|
+
console.log(`[Daemon] Cached machine UUID to config: ${machineUuid}`);
|
|
9043
9091
|
} catch (error) {
|
|
9044
|
-
console.warn("[Daemon] Failed to cache
|
|
9092
|
+
console.warn("[Daemon] Failed to cache machine UUID:", error instanceof Error ? error.message : error);
|
|
9045
9093
|
}
|
|
9046
9094
|
}
|
|
9047
9095
|
/**
|
|
@@ -9102,14 +9150,14 @@ var Daemon = class _Daemon {
|
|
|
9102
9150
|
*/
|
|
9103
9151
|
async syncMachineProjectPath(projectId, projectPath) {
|
|
9104
9152
|
try {
|
|
9105
|
-
if (!this.
|
|
9106
|
-
console.warn("[Daemon] EP995: Cannot sync project path -
|
|
9153
|
+
if (!this.machineUuid) {
|
|
9154
|
+
console.warn("[Daemon] EP995: Cannot sync project path - machineUuid not available");
|
|
9107
9155
|
return;
|
|
9108
9156
|
}
|
|
9109
9157
|
const config = await (0, import_core12.loadConfig)();
|
|
9110
9158
|
if (!config) return;
|
|
9111
9159
|
const apiUrl = config.api_url || "https://episoda.dev";
|
|
9112
|
-
const response = await fetchWithAuth(`${apiUrl}/api/account/machines/${this.
|
|
9160
|
+
const response = await fetchWithAuth(`${apiUrl}/api/account/machines/${this.machineUuid}`, {
|
|
9113
9161
|
method: "PATCH",
|
|
9114
9162
|
headers: {
|
|
9115
9163
|
"Content-Type": "application/json"
|
|
@@ -9190,8 +9238,8 @@ var Daemon = class _Daemon {
|
|
|
9190
9238
|
async reconcileWorktrees(projectId, projectPath, client) {
|
|
9191
9239
|
console.log(`[Daemon] EP1003: Starting reconciliation report for project ${projectId}`);
|
|
9192
9240
|
try {
|
|
9193
|
-
if (!this.
|
|
9194
|
-
console.log("[Daemon] EP1003: Cannot reconcile -
|
|
9241
|
+
if (!this.machineUuid) {
|
|
9242
|
+
console.log("[Daemon] EP1003: Cannot reconcile - machineUuid not available yet");
|
|
9195
9243
|
return;
|
|
9196
9244
|
}
|
|
9197
9245
|
const config = await (0, import_core12.loadConfig)();
|
|
@@ -9202,7 +9250,7 @@ var Daemon = class _Daemon {
|
|
|
9202
9250
|
let modulesResponse;
|
|
9203
9251
|
try {
|
|
9204
9252
|
modulesResponse = await fetchWithAuth(
|
|
9205
|
-
`${apiUrl}/api/modules?state=doing,review&dev_mode=local&checkout_machine_id=${this.
|
|
9253
|
+
`${apiUrl}/api/modules?state=doing,review&dev_mode=local&checkout_machine_id=${this.machineUuid}&project_id=${projectId}`,
|
|
9206
9254
|
{ signal: controller.signal }
|
|
9207
9255
|
);
|
|
9208
9256
|
} finally {
|
|
@@ -9251,7 +9299,7 @@ var Daemon = class _Daemon {
|
|
|
9251
9299
|
}
|
|
9252
9300
|
const report = {
|
|
9253
9301
|
projectId,
|
|
9254
|
-
machineId: this.
|
|
9302
|
+
machineId: this.machineUuid,
|
|
9255
9303
|
modules: moduleStatuses,
|
|
9256
9304
|
orphanTunnels: orphanTunnels.length > 0 ? orphanTunnels : void 0
|
|
9257
9305
|
};
|
|
@@ -9272,8 +9320,11 @@ var Daemon = class _Daemon {
|
|
|
9272
9320
|
/**
|
|
9273
9321
|
* EP1047: Process pending cleanup queue entries for this machine
|
|
9274
9322
|
*
|
|
9275
|
-
*
|
|
9276
|
-
*
|
|
9323
|
+
* @deprecated EP1091: No longer called on connect. Server-side cron now processes
|
|
9324
|
+
* the cleanup queue and sends WebSocket commands to connected daemons. This is
|
|
9325
|
+
* more reliable than daemon-side polling since it works even if daemon stays connected.
|
|
9326
|
+
*
|
|
9327
|
+
* Kept for potential manual invocation or debugging purposes.
|
|
9277
9328
|
*
|
|
9278
9329
|
* Flow:
|
|
9279
9330
|
* 1. Query server for pending cleanup tasks for this machine
|
|
@@ -9281,11 +9332,11 @@ var Daemon = class _Daemon {
|
|
|
9281
9332
|
* 3. Report success/failure back to server
|
|
9282
9333
|
*/
|
|
9283
9334
|
async reconcilePendingCleanups(projectId, projectPath) {
|
|
9284
|
-
if (!this.
|
|
9285
|
-
console.log("[Daemon] EP1047: Cannot reconcile cleanups -
|
|
9335
|
+
if (!this.machineUuid) {
|
|
9336
|
+
console.log("[Daemon] EP1047: Cannot reconcile cleanups - machineUuid not available yet");
|
|
9286
9337
|
return;
|
|
9287
9338
|
}
|
|
9288
|
-
console.log(`[Daemon] EP1047: Checking for pending cleanup tasks for machine ${this.
|
|
9339
|
+
console.log(`[Daemon] EP1047: Checking for pending cleanup tasks for machine ${this.machineUuid}`);
|
|
9289
9340
|
try {
|
|
9290
9341
|
const config = await (0, import_core12.loadConfig)();
|
|
9291
9342
|
if (!config) {
|
|
@@ -9298,7 +9349,7 @@ var Daemon = class _Daemon {
|
|
|
9298
9349
|
let response;
|
|
9299
9350
|
try {
|
|
9300
9351
|
response = await fetchWithAuth(
|
|
9301
|
-
`${apiUrl}/api/cli/background-ops?machine_id=${this.
|
|
9352
|
+
`${apiUrl}/api/cli/background-ops?machine_id=${this.machineUuid}`,
|
|
9302
9353
|
{ signal: controller.signal }
|
|
9303
9354
|
);
|
|
9304
9355
|
} finally {
|