Job 1788306156260

Command: powershell -NoProfile -Command "Get-Content -LiteralPath '..\apps-script\Code.gs' | Select-Object -First 190"
Directory: projects
Status: SUCCESS
Exit code: 0

Cancel Job Rerun Command Refresh

const SPREADSHEET_ID = '1HkbrZ7Phos3GVMBhi-1xPDr3wIBxRcBF6mNzabYqhmE';
const COMMANDS_SHEET = 'commands';
const RESULTS_SHEET = 'results';
const STATE_SHEET = 'state';
const ARTIFACTS_SHEET = 'artifacts';
const MAX_CELL_CHARS = 45000;

function doGet(e) {
  return handleRequest_(e, null);
}

function doPost(e) {
  let body = {};
  if (e && e.postData && e.postData.contents) {
    try {
      body = JSON.parse(e.postData.contents);
    } catch (err) {
      return json_({ ok: false, error: 'invalid_json' }, 400);
    }
  }
  return handleRequest_(e, body);
}

function handleRequest_(e, body) {
  const params = (e && e.parameter) || {};
  const action = (body && body.action) || params.action || 'status';
  const key = (body && (body.relay_key || body.key)) || params.relay_key || params.key || '';
  if (!validKey_(key)) {
    return json_({ ok: false, error: 'not_found' }, 404);
  }

  const lock = LockService.getScriptLock();
  lock.waitLock(10000);
  try {
    ensureSheets_();
    if (action === 'status') return status_();
    if (action === 'poll') return poll_();
    if (action === 'result') return result_(body || {});
    if (action === 'heartbeat') return heartbeat_(body || {});
    return json_({ ok: false, error: 'unknown_action' }, 400);
  } catch (err) {
    return json_({ ok: false, error: String(err && err.message ? err.message : err) }, 500);
  } finally {
    lock.releaseLock();
  }
}

function validKey_(key) {
  const expected = getStateValue_('relay_key') || PropertiesService.getScriptProperties().getProperty('BRIDGE_RELAY_KEY');
  return Boolean(expected && key && key === expected);
}

function poll_() {
  const sheet = sheet_(COMMANDS_SHEET);
  const values = sheet.getDataRange().getValues();
  const headers = values[0] || [];
  const col = headerMap_(headers);
  for (let r = 1; r < values.length; r++) {
    const row = values[r];
    const status = String(row[col.status] || '').toLowerCase();
    const confirm = String(row[col.confirm] || '').toLowerCase();
    if (status === 'pending' && confirm === 'yes') {
      const attempts = Number(row[col.attempts] || 0) + 1;
      sheet.getRange(r + 1, col.status + 1).setValue('running');
      sheet.getRange(r + 1, col.claimed_at + 1).setValue(nowIso_());
      sheet.getRange(r + 1, col.attempts + 1).setValue(attempts);
      return json_({
        ok: true,
        command: {
          row: r + 1,
          id: String(row[col.id] || ''),
          action: String(row[col.action] || ''),
          payload_json: String(row[col.payload_json] || '{}'),
          attempts: attempts
        }
      });
    }
  }
  return json_({ ok: true, command: null });
}

function result_(body) {
  const commandId = String(body.command_id || '');
  if (!commandId) return json_({ ok: false, error: 'command_id_required' }, 400);

  const status = String(body.status || 'done');
  const completedAt = nowIso_();
  const resultText = truncate_(String(body.result_text || ''));
  const errorText = truncate_(String(body.error || ''));
  const httpStatus = body.http_status === undefined ? '' : String(body.http_status);
  const durationMs = body.duration_ms === undefined ? '' : String(body.duration_ms);
  const sha = String(body.result_sha256 || '');
  const truncated = body.truncated ? 'yes' : 'no';

  const resultSheet = sheet_(RESULTS_SHEET);
  const resultId = Utilities.getUuid();
  resultSheet.appendRow([resultId, commandId, completedAt, status, httpStatus, resultText, errorText, durationMs, sha, truncated]);

  const commandSheet = sheet_(COMMANDS_SHEET);
  const rows = commandSheet.getDataRange().getValues();
  const col = headerMap_(rows[0] || []);
  for (let r = 1; r < rows.length; r++) {
    if (String(rows[r][col.id] || '') === commandId) {
      commandSheet.getRange(r + 1, col.status + 1).setValue(status);
      commandSheet.getRange(r + 1, col.completed_at + 1).setValue(completedAt);
      commandSheet.getRange(r + 1, col.result_ref + 1).setValue(resultId);
      commandSheet.getRange(r + 1, col.error + 1).setValue(errorText);
      break;
    }
  }
  setStateValue_('last_command_id', commandId, 'Updated by relay result');
  return json_({ ok: true, result_id: resultId });
}

function heartbeat_(body) {
  setStateValue_('relay_status', String(body.relay_status || 'ONLINE'), 'Updated by laptop relay');
  setStateValue_('bridge_local', String(body.bridge_local || 'UNKNOWN'), '127.0.0.1 bridge health from relay');
  setStateValue_('relay_version', String(body.relay_version || ''), 'Sheet relay package revision');
  return json_({ ok: true, updated_at: nowIso_() });
}

function status_() {
  return json_({
    ok: true,
    status: getStateValue_('relay_status') || 'UNKNOWN',
    bridge_local: getStateValue_('bridge_local') || 'UNKNOWN',
    updated_at: nowIso_()
  });
}

function ensureSheets_() {
  const ss = SpreadsheetApp.openById(SPREADSHEET_ID);
  ensureSheet_(ss, COMMANDS_SHEET, ['id', 'created_at', 'action', 'payload_json', 'status', 'confirm', 'claimed_at', 'completed_at', 'result_ref', 'error', 'attempts', 'notes']);
  ensureSheet_(ss, RESULTS_SHEET, ['id', 'command_id', 'completed_at', 'status', 'http_status', 'result_text', 'error', 'duration_ms', 'result_sha256', 'truncated']);
  ensureSheet_(ss, STATE_SHEET, ['key', 'value', 'updated_at', 'notes']);
  ensureSheet_(ss, ARTIFACTS_SHEET, ['id', 'command_id', 'name', 'mime_type', 'size', 'sha256', 'drive_url_or_ref', 'created_at']);
}

function ensureSheet_(ss, name, headers) {
  let sh = ss.getSheetByName(name);
  if (!sh) sh = ss.insertSheet(name);
  const current = sh.getRange(1, 1, 1, headers.length).getValues()[0];
  let needsHeader = false;
  for (let i = 0; i < headers.length; i++) {
    if (String(current[i] || '') !== headers[i]) needsHeader = true;
  }
  if (needsHeader) {
    sh.getRange(1, 1, 1, headers.length).setValues([headers]);
    sh.getRange(1, 1, 1, headers.length).setFontWeight('bold');
    sh.setFrozenRows(1);
  }
}

function sheet_(name) {
  const sh = SpreadsheetApp.openById(SPREADSHEET_ID).getSheetByName(name);
  if (!sh) throw new Error('missing sheet: ' + name);
  return sh;
}

function headerMap_(headers) {
  const out = {};
  headers.forEach(function (h, i) { out[String(h)] = i; });
  return out;
}

function getStateValue_(key) {
  const sh = sheet_(STATE_SHEET);
  const rows = sh.getDataRange().getValues();
  for (let i = 1; i < rows.length; i++) {
    if (String(rows[i][0]) === key) return String(rows[i][1] || '');
  }
  return '';
}

function setStateValue_(key, value, notes) {
  const sh = sheet_(STATE_SHEET);
  const rows = sh.getDataRange().getValues();
  for (let i = 1; i < rows.length; i++) {
    if (String(rows[i][0]) === key) {
      sh.getRange(i + 1, 2, 1, 3).setValues([[value, nowIso_(), notes || '']]);
      return;
    }
  }
  sh.appendRow([key, value, nowIso_(), notes || '']);
}

function json_(obj, statusCode) {
  return ContentService
    .createTextOutput(JSON.stringify(obj))
    .setMimeType(ContentService.MimeType.JSON);