@cluesmith/codev 2.0.0-rc.28 → 2.0.0-rc.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cluesmith/codev",
3
- "version": "2.0.0-rc.28",
3
+ "version": "2.0.0-rc.29",
4
4
  "description": "Codev CLI - AI-assisted software development framework",
5
5
  "type": "module",
6
6
  "bin": {
@@ -20,7 +20,7 @@ async function closeTab(tabId, event) {
20
20
  // Check if process is still running before showing confirmation (Bugfix #132)
21
21
  // If the shell/builder already exited, close immediately without confirmation
22
22
  try {
23
- const response = await fetch(`/api/tabs/${encodeURIComponent(tabId)}/running`);
23
+ const response = await fetch(apiUrl(`api/tabs/${encodeURIComponent(tabId)}/running`));
24
24
  if (response.ok) {
25
25
  const { running } = await response.json();
26
26
  if (!running) {
@@ -56,7 +56,7 @@ async function doCloseTab(tabId) {
56
56
  if (!tab) return;
57
57
 
58
58
  try {
59
- await fetch(`/api/tabs/${encodeURIComponent(tabId)}`, { method: 'DELETE' });
59
+ await fetch(apiUrl(`api/tabs/${encodeURIComponent(tabId)}`), { method: 'DELETE' });
60
60
 
61
61
  tabs = tabs.filter(t => t.id !== tabId);
62
62
 
@@ -224,7 +224,7 @@ async function createFile() {
224
224
  }
225
225
 
226
226
  try {
227
- const response = await fetch('/api/files', {
227
+ const response = await fetch(apiUrl('api/files'), {
228
228
  method: 'POST',
229
229
  headers: { 'Content-Type': 'application/json' },
230
230
  body: JSON.stringify({ path: filePath, content: '' })
@@ -251,7 +251,7 @@ async function createFile() {
251
251
  // Spawn worktree builder
252
252
  async function spawnBuilder() {
253
253
  try {
254
- const response = await fetch('/api/tabs/builder', {
254
+ const response = await fetch(apiUrl('api/tabs/builder'), {
255
255
  method: 'POST',
256
256
  headers: { 'Content-Type': 'application/json' },
257
257
  body: JSON.stringify({})
@@ -283,7 +283,7 @@ async function spawnBuilder() {
283
283
  // Spawn shell
284
284
  async function spawnShell() {
285
285
  try {
286
- const response = await fetch('/api/tabs/shell', {
286
+ const response = await fetch(apiUrl('api/tabs/shell'), {
287
287
  method: 'POST',
288
288
  headers: { 'Content-Type': 'application/json' },
289
289
  body: JSON.stringify({})
@@ -325,7 +325,7 @@ async function spawnShell() {
325
325
  // Create new utility shell (quick action button)
326
326
  async function createNewShell() {
327
327
  try {
328
- const response = await fetch('/api/tabs/shell', { method: 'POST' });
328
+ const response = await fetch(apiUrl('api/tabs/shell'), { method: 'POST' });
329
329
  const data = await response.json();
330
330
  if (!data.success && data.error) {
331
331
  showToast(data.error || 'Failed to create shell', 'error');
@@ -347,7 +347,7 @@ async function createNewWorktreeShell() {
347
347
  if (branch === null) return;
348
348
 
349
349
  try {
350
- const response = await fetch('/api/tabs/shell', {
350
+ const response = await fetch(apiUrl('api/tabs/shell'), {
351
351
  method: 'POST',
352
352
  headers: { 'Content-Type': 'application/json' },
353
353
  body: JSON.stringify({ worktree: true, branch: branch || undefined })
@@ -22,7 +22,7 @@ function stopFilesPolling() {
22
22
  // Check if files have changed and refresh if needed
23
23
  async function checkFilesForChanges() {
24
24
  try {
25
- const response = await fetch(`/api/files/hash?t=${Date.now()}`);
25
+ const response = await fetch(apiUrl(`api/files/hash?t=${Date.now()}`));
26
26
  if (!response.ok) return;
27
27
 
28
28
  const data = await response.json();
@@ -42,7 +42,7 @@ async function checkFilesForChanges() {
42
42
  async function loadFilesTree() {
43
43
  try {
44
44
  // Add cache-busting param to ensure fresh data
45
- const response = await fetch(`/api/files?t=${Date.now()}`);
45
+ const response = await fetch(apiUrl(`api/files?t=${Date.now()}`));
46
46
  if (!response.ok) {
47
47
  throw new Error(`HTTP ${response.status}: ${response.statusText}`);
48
48
  }
@@ -34,7 +34,7 @@ async function openFileFromMessage(filePath, lineNumber) {
34
34
  // Refresh state from API
35
35
  async function refresh() {
36
36
  try {
37
- const response = await fetch('/api/state');
37
+ const response = await fetch(apiUrl('api/state'));
38
38
  if (!response.ok) throw new Error('Failed to fetch state');
39
39
 
40
40
  const newState = await response.json();
@@ -65,7 +65,7 @@ function stopPolling() {
65
65
  // Poll for projectlist.md creation when in starter mode
66
66
  async function pollForProjectlistCreation() {
67
67
  try {
68
- const response = await fetch('/api/projectlist-exists');
68
+ const response = await fetch(apiUrl('api/projectlist-exists'));
69
69
  if (!response.ok) return;
70
70
 
71
71
  const { exists } = await response.json();
@@ -420,7 +420,7 @@ async function pollHotReload() {
420
420
  if (!hotReloadEnabled) return;
421
421
 
422
422
  try {
423
- const response = await fetch('/api/hot-reload');
423
+ const response = await fetch(apiUrl('api/hot-reload'));
424
424
  if (!response.ok) return;
425
425
 
426
426
  const data = await response.json();
@@ -140,7 +140,7 @@ function parseProjectlist(content) {
140
140
  // Load projectlist.md from disk
141
141
  async function loadProjectlist() {
142
142
  try {
143
- const response = await fetch('/file?path=codev/projectlist.md');
143
+ const response = await fetch(apiUrl('file?path=codev/projectlist.md'));
144
144
 
145
145
  if (!response.ok) {
146
146
  if (response.status === 404) {
@@ -181,7 +181,7 @@ async function pollProjectlist() {
181
181
  if (activeTabId !== 'dashboard') return;
182
182
 
183
183
  try {
184
- const response = await fetch('/file?path=codev/projectlist.md');
184
+ const response = await fetch(apiUrl('file?path=codev/projectlist.md'));
185
185
  if (!response.ok) return;
186
186
 
187
187
  const text = await response.text();
@@ -300,7 +300,7 @@ function renderStageCell(project, stage) {
300
300
  // Open a project file in a new annotation tab
301
301
  async function openProjectFile(path) {
302
302
  try {
303
- const response = await fetch('/api/tabs/file', {
303
+ const response = await fetch(apiUrl('api/tabs/file'), {
304
304
  method: 'POST',
305
305
  headers: { 'Content-Type': 'application/json' },
306
306
  body: JSON.stringify({ path })
@@ -1,5 +1,26 @@
1
1
  // Dashboard Utility Functions
2
2
 
3
+ // Get the base path for API calls (handles tower proxy context)
4
+ // When accessed via tower proxy at /project/<encoded>/, API calls need to go through the proxy
5
+ // When accessed directly at /, API calls go to root
6
+ function getApiBase() {
7
+ const path = window.location.pathname;
8
+ // Check if we're in tower proxy context: /project/<encoded>/
9
+ const match = path.match(/^(\/project\/[^/]+\/)/);
10
+ if (match) {
11
+ return match[1]; // Returns /project/<encoded>/
12
+ }
13
+ return '/'; // Direct access
14
+ }
15
+
16
+ // Build API URL that works both directly and through tower proxy
17
+ function apiUrl(endpoint) {
18
+ const base = getApiBase();
19
+ // Remove leading slash from endpoint if present
20
+ const cleanEndpoint = endpoint.startsWith('/') ? endpoint.slice(1) : endpoint;
21
+ return base + cleanEndpoint;
22
+ }
23
+
3
24
  // Escape HTML special characters to prevent XSS
4
25
  function escapeHtml(text) {
5
26
  return String(text)
@@ -87,7 +108,7 @@ async function openFileTab(filePath, options = {}) {
87
108
  }
88
109
 
89
110
  // Create new tab
90
- const response = await fetch('/api/tabs/file', {
111
+ const response = await fetch(apiUrl('api/tabs/file'), {
91
112
  method: 'POST',
92
113
  headers: { 'Content-Type': 'application/json' },
93
114
  body: JSON.stringify({ path: filePath })