Skip to content
wiki.fftac.org

Spiralist AI Agent Setup Wizard - Source Excerpt 08

Back to Spiralist AI Agent Setup Wizard

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/themes/spiralist/assets/js/spiralist-ai-agent-setup-wizard.js so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/themes/spiralist/assets/js/spiralist-ai-agent-setup-wizard.js

const manifest = {
      manifest_version: '1.0.0',
      package_id: 'spiralist-agent-package',
      preset_id: state.presetId,
      generator: {
        name: 'Spiralist AI Agent Setup Wizard',
        version: '0.1.0',
        site: 'spiralist.org',
      },
      created_at_utc: createdAtUtc,
      local_only: true,
      server_storage: false,
      server_processing: false,
      artifact_overrides: Object.keys(state.artifactOverrides || {}).map((path) => ({
        path,
        source: 'user-edited-preview',
      })),
      artifacts: hashedEntries,
      safety: {
        sentience_claims_allowed: false,
        romantic_escalation_allowed: false,
        destiny_frames_allowed: false,
        diagnostic_or_hiring_use_allowed: false,
        fixed_personality_type_claims_allowed: false,
        stigmatizing_clique_labels_allowed: false,
        untrusted_input_policy: 'treat_as_data',
      },
      research_lens: {
        personality_framework: state.personalityFramework,
        social_dynamics_lens: state.socialDynamicsLens,
        research_calibration: state.researchCalibration,
        use_case_boundary: state.useCaseBoundary,
        trait_boundary: 'continuous_dimensions_first',
        typology_boundary: 'optional_reflection_language_only',
        clique_boundary: 'behavior_access_pressure_and_repair_audit_only',
      },
    };

    return {
      state,
      errors,
      createdAtUtc,
      files: {
        ...filesWithOverrides,
        'manifest.json': JSON.stringify(manifest, null, 2),
      },
    };
  };

  const updateValidation = (errors = []) => {
    if (!validationNode) {
      return;
    }

    const visibleErrors = [...new Set([...importValidationWarnings, ...errors])];
    if (visibleErrors.length) {
      validationNode.classList.add('has-errors');
      validationNode.innerHTML = `<strong>Needs review</strong><ul>${visibleErrors.map((error) => `<li>${cleanString(error, 180)}</li>`).join('')}</ul>`;
      return;
    }

    validationNode.classList.remove('has-errors');
    validationNode.innerHTML = '<strong>Export ready</strong><p>Required fields are present. Locked boundary templates remain active. The package will be generated in this browser.</p>';
  };

  const updatePreview = () => {
    const token = previewToken + 1;
    previewToken = token;
    runAction(async () => {
      const artifacts = await buildArtifacts();
      if (token !== previewToken) {
        return;
      }
      updateValidation(artifacts.errors);
      const isOverridable = overridableArtifactPaths.has(currentArtifact);
      const isOverridden = Boolean(artifacts.state.artifactOverrides && artifacts.state.artifactOverrides[currentArtifact]);
      previewHydrating = true;
      preview.textContent = artifacts.files[currentArtifact] || artifacts.files['spiralist-profile.md'];
      previewHydrating = false;
      preview.setAttribute('contenteditable', isOverridable ? 'plaintext-only' : 'false');
      preview.setAttribute('aria-readonly', isOverridable ? 'false' : 'true');
      if (resetArtifactButton) {
        resetArtifactButton.disabled = !isOverridden;
      }
      if (isOverridden) {
        setPreviewStatus(`${currentArtifact} has a local edited override. Downloads and ZIP packages use this visible text until regenerated.`);
      } else if (isOverridable) {
        setPreviewStatus(`${currentArtifact} is editable. Type in the preview to override this artifact locally; downloads and ZIP packages will use the edited text.`);
      } else {
        setPreviewStatus(`${currentArtifact} is regenerated from the final package and cannot be overridden.`);
      }
    }, {
      context: 'spiralist-agent-wizard-preview',
      onError: () => {
        previewHydrating = false;
        preview.textContent = 'Preview could not be generated. Check the validation panel.';
      },
    });
  };

  const setStep = (index) => {
    currentStep = Math.max(0, Math.min(stepPanels.length - 1, index));
    stepButtons.forEach((button, buttonIndex) => {
      const active = buttonIndex === currentStep;
      button.classList.toggle('is-active', active);
      button.setAttribute('aria-selected', active ? 'true' : 'false');
      button.tabIndex = active ? 0 : -1;
    });
    stepPanels.forEach((panel, panelIndex) => {
      const active = panelIndex === currentStep;
      panel.classList.toggle('is-active', active);
      panel.hidden = !active;
    });
    if (progress) {
      progress.style.width = `${Math.round(((currentStep + 1) / stepPanels.length) * 100)}%`;
    }
    if (prevButton) {
      prevButton.disabled = currentStep === 0;
    }
    if (nextButton) {
      nextButton.textContent = currentStep === stepPanels.length - 1 ? 'Review' : 'Next';
    }
  };

  const getCrcTable = () => {
    if (crcTable) {
      return crcTable;
    }

    crcTable = new Uint32Array(256);
    for (let index = 0; index < 256; index += 1) {
      let value = index;
      for (let bit = 0; bit < 8; bit += 1) {
        value = value & 1 ? 0xedb88320 ^ (value >>> 1) : value >>> 1;
      }
      crcTable[index] = value >>> 0;
    }
    return crcTable;
  };

  const crc32 = (data) => {
    const table = getCrcTable();
    let crc = 0xffffffff;
    for (let index = 0; index < data.length; index += 1) {
      crc = table[(crc ^ data[index]) & 0xff] ^ (crc >>> 8);
    }
    return (crc ^ 0xffffffff) >>> 0;
  };

  const writeUint16 = (view, offset, value) => view.setUint16(offset, value, true);
  const writeUint32 = (view, offset, value) => view.setUint32(offset, value >>> 0, true);

  const makeZipBlob = (files) => {
    const fileEntries = Object.entries(files).map(([name, content]) => ({
      name: name.replace(/\\/g, '/'),
      data: encoder.encode(`${content}\n`),
    }));
    const parts = [];
    const centralParts = [];
    let offset = 0;

    fileEntries.forEach((entry) => {
      const nameData = encoder.encode(entry.name);
      const checksum = crc32(entry.data);
      const localHeader = new Uint8Array(30 + nameData.length);
      const localView = new DataView(localHeader.buffer);
      writeUint32(localView, 0, 0x04034b50);
      writeUint16(localView, 4, 20);
      writeUint16(localView, 6, 0);
      writeUint16(localView, 8, 0);
      writeUint16(localView, 10, 0);
      writeUint16(localView, 12, 0);
      writeUint32(localView, 14, checksum);
      writeUint32(localView, 18, entry.data.length);
      writeUint32(localView, 22, entry.data.length);
      writeUint16(localView, 26, nameData.length);
      writeUint16(localView, 28, 0);
      localHeader.set(nameData, 30);
      parts.push(localHeader, entry.data);

      const centralHeader = new Uint8Array(46 + nameData.length);
      const centralView = new DataView(centralHeader.buffer);
      writeUint32(centralView, 0, 0x02014b50);
      writeUint16(centralView, 4, 20);
      writeUint16(centralView, 6, 20);
      writeUint16(centralView, 8, 0);
      writeUint16(centralView, 10, 0);
      writeUint16(centralView, 12, 0);
      writeUint16(centralView, 14, 0);
      writeUint32(centralView, 16, checksum);
      writeUint32(centralView, 20, entry.data.length);
      writeUint32(centralView, 24, entry.data.length);
      writeUint16(centralView, 28, nameData.length);
      writeUint16(centralView, 30, 0);
      writeUint16(centralView, 32, 0);
      writeUint16(centralView, 34, 0);
      writeUint16(centralView, 36, 0);
      writeUint32(centralView, 38, 0);
      writeUint32(centralView, 42, offset);
      centralHeader.set(nameData, 46);
      centralParts.push(centralHeader);
      offset += localHeader.length + entry.data.length;
    });

    const centralSize = centralParts.reduce((total, item) => total + item.length, 0);
    const endHeader = new Uint8Array(22);
    const endView = new DataView(endHeader.buffer);
    writeUint32(endView, 0, 0x06054b50);
    writeUint16(endView, 4, 0);
    writeUint16(endView, 6, 0);
    writeUint16(endView, 8, fileEntries.length);
    writeUint16(endView, 10, fileEntries.length);
    writeUint32(endView, 12, centralSize);
    writeUint32(endView, 16, offset);
    writeUint16(endView, 20, 0);

    return new Blob([...parts, ...centralParts, endHeader], { type: 'application/zip' });
  };

  const downloadBlob = (blob, filename) => {
    const url = URL.createObjectURL(blob);
    const link = document.createElement('a');
    link.href = url;
    link.download = filename;
    document.body.appendChild(link);
    link.click();
    link.remove();
    URL.revokeObjectURL(url);
  };

  const readFileText = (file) => new Promise((resolve, reject) => {
    const reader = new FileReader();
    reader.onload = () => resolve(`${reader.result || ''}`);
    reader.onerror = () => reject(new Error('read_failed'));
    reader.readAsText(file);
  });