diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index d4177c3..8e4db01 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -28,7 +28,7 @@ assignees: "" ## Environment -- Tool and version: {e.g. ghl-workflow-exporter 1.0.0 — see its manifest.json} +- Tool and version: {e.g. ghl-workflow-tools 1.1.0 — see its manifest.json} - Browser: {browser_and_version} - HighLevel app domain: {app.gohighlevel.com or your white-labelled domain — never paste tokens} diff --git a/CHANGELOG.md b/CHANGELOG.md index 31c4f99..eb7d470 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,7 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added - Repository baseline: per-tool layout, CI manifest validation, CODEOWNERS, issue/PR templates, security policy, AGPL-3.0 license. -- `ghl-workflow-exporter` — first tool in the set: exports every workflow in the current HighLevel sub-account as re-importable JSON, packaged as a deterministic ZIP for version control. +- `ghl-workflow-tools` ("GHL Workflow Backup") — first tool in the set: exports every workflow in the current HighLevel sub-account as re-importable JSON, packaged as a deterministic ZIP for version control, and restores workflows from a backup as new drafts or in-place overwrites (overwriting a published workflow requires an explicit acknowledgement). +- Legion Code Inc. branding across the root and tool READMEs, extension manifest, and popup. [Unreleased]: https://github.com/legioncodeinc/ghl-toolset/compare/v0.1.0...HEAD [0.1.0]: https://github.com/legioncodeinc/ghl-toolset/releases/tag/v0.1.0 diff --git a/CLAUDE.md b/CLAUDE.md index 2a48141..b7c8c0f 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ Guidance for AI coding agents working in this repository. Read this before touch A **multi-tool set of Chrome extensions for GoHighLevel (HighLevel) sub-accounts** — one focused, standalone extension per facet of an account (workflows today; pipelines, calendars, custom values, etc. as the set grows). It is deliberately **not** a single bulk tool that migrates or dumps everything from a sub-account. Never propose merging tools or adding "modes" to an existing tool; a new facet means a new tool folder. -The first tool is [`ghl-workflow-exporter/`](ghl-workflow-exporter/). The direction: a tool for every facet of the account that can be read through the app's own session. +The first tool is [`ghl-workflow-tools/`](ghl-workflow-tools/) — "GHL Workflow Backup": exports and restores workflows. The direction: a tool for every facet of the account that can be read through the app's own session. **Positioning:** the project is published for **R&D purposes only** — it rides on undocumented, internal HighLevel endpoints and is not affiliated with HighLevel. Keep that framing in user-facing docs; never imply production support or endorsement. @@ -28,9 +28,10 @@ Every tool follows the same shape (the contract is also documented in the root R ghl--/ manifest.json # MV3; permissions: activeTab, scripting, downloads — nothing else popup.html/.css # UI shell - popup.js # orchestration: probes tab, drives export loop, builds files, downloads + popup.js # orchestration: probes tab, drives export/import loops, builds files, downloads agent.js # functions injected into the page's MAIN world zip.js # dependency-free ZIP writer (STORE method, fixed 1980-01-01 timestamp) + unzip.js # dependency-free ZIP reader (browser DecompressionStream) — only where a tool reads backups back icons/ README.md # the tool's own docs ``` @@ -41,7 +42,7 @@ ghl--/ 2. **Never read, store, or transmit credentials.** Authentication is borrowed: prefer `window.SHELL_STORE.$http` (the app's axios instance whose interceptor attaches the session token). Fallback: locate the session JWT inside the page's Vuex auth state and use it *only within the page*, never exfiltrate it. This is the design promise of the whole toolset. -3. **Read-only.** Every request a tool makes is a GET. Never add POST/PUT/DELETE calls to a sub-account. +3. **Exports are read-only; writes are explicit and guarded.** Every request an *export* makes is a GET. Write operations (POST/PUT/DELETE) are allowed only in tools whose purpose includes restore/import, and only as explicit, user-initiated actions. The safety rails are non-negotiable: restores default to creating **drafts**; overwriting an existing workflow is opt-in; overwriting a **published** one requires an extra acknowledgement; nothing is ever published automatically. Never add a write that fires as a side effect of an export or probe. 4. **Minimal permissions.** `activeTab`, `scripting`, `downloads` only. No `host_permissions`, no background service worker, no remote code. @@ -53,8 +54,10 @@ ghl--/ ## GHL API facts (learned, currently load-bearing) -- List workflows: `GET backend.leadconnectorhq.com/workflow/{locationId}/list` with `limit`/`skip` pagination (`limit: 200` per page; stop when `rows.length >= count`). +- List workflows: `GET backend.leadconnectorhq.com/workflow/{locationId}/list` paginated with `limit`/`offset` (`limit: 200` per page; stop when `rows.length >= count`). **Not `skip`** — the endpoint silently ignores it and re-returns page one; also guard against repeated ids when paging. - Full workflow definition: `GET backend.leadconnectorhq.com/workflow/{locationId}/{workflowId}?includeTriggers=true` — `includeTriggers=true` is what swaps bare metadata for the `{ workflowData, triggers, dependentAssets }` shape the workflow builder's JSON import accepts. +- Restore/import path (mirrors the app's own restore-from-nodes flow): create via `POST .../workflow/{locationId}` (returns `{ id, assetWarnings }`), delete the target's existing triggers, re-create the incoming triggers (ids stripped, `workflowId` and `actions[].workflow_id` rewired), read them back, then `PUT` the action graph with **`isRestoreRequest: true`** and `newTriggers` set. +- The restore `PUT` must carry the **target's current `version`** (optimistic concurrency — a stale number fails with *"Your version is outdated"*), and `timezone` is an enum (`"account"` etc.), not an IANA name; both fail loudly if wrong. - Raw-fetch fallback headers (when not using `$http`): `Authorization: Bearer `, `channel: APP`, `source: WEB_USER`, `Version: 2021-07-28`. - Detecting the sub-account: `window.SHELL_STORE.state.locations.currentLocation` (`.id`/`._id`, `.name`), with the URL path `/location/` as a secondary source. - HighLevel rate-limits bursts per location. Keep the courtesy pause between per-item requests (~120ms) and the retry-with-backoff pattern (3 attempts, 400ms·n) when adding new export loops. @@ -62,7 +65,7 @@ ghl--/ ## Adding a new tool -1. Create `ghl--/` at the repo root with the anatomy above (copy `ghl-workflow-exporter` as the starting skeleton). +1. Create `ghl--/` at the repo root with the anatomy above (copy `ghl-workflow-tools` as the starting skeleton). 2. Give it its own `manifest.json`, icons, and README following the existing tool's README structure (Install / Use / What comes out / How it works / Design notes / Limits). 3. Add a row to the tool table in the root `README.md` (move the facet out of the roadmap list). 4. `scripts/validate-manifests.mjs` picks up any `*/manifest.json` automatically — no registration needed. diff --git a/README.md b/README.md index 180dd0c..9091198 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # GHL Toolset +**By [Legion Code Inc.](https://github.com/legioncodeinc)** + [![CI](https://img.shields.io/github/actions/workflow/status/legioncodeinc/ghl-toolset/ci.yml?branch=main&label=CI)](https://github.com/legioncodeinc/ghl-toolset/actions/workflows/ci.yml) [![License](https://img.shields.io/github/license/legioncodeinc/ghl-toolset)](https://github.com/legioncodeinc/ghl-toolset/blob/main/LICENSE) -A growing set of small Chrome extensions that export, back up, and version-control every facet of a GoHighLevel (HighLevel) sub-account — one focused tool per facet, for agencies and operators who keep their sub-account configuration in git. +A growing set of small Chrome extensions that export, back up, restore, and version-control every facet of a GoHighLevel (HighLevel) sub-account — one focused tool per facet, for agencies and operators who keep their sub-account configuration in git. > **R&D purposes only.** This toolset is built and published for research and development purposes only. The tools talk to undocumented, internal HighLevel endpoints that can change or break without notice, and this project is not affiliated with or endorsed by HighLevel. Verify everything a tool produces before relying on it anywhere real. @@ -15,10 +17,10 @@ This is a deliberate design choice: rather than one bulk extension that migrates The first tool is live: -| Tool | Status | What it does | -| --- | --- | --- | -| [`ghl-workflow-exporter`](./ghl-workflow-exporter/) | Available | Exports every workflow in the current sub-account as re-importable JSON in a ZIP | -| `ghl-*` — pipelines, calendars, custom values, templates, funnels, forms, and the rest of the account | Planned | One focused tool per facet; see [Roadmap](#roadmap) | +| Tool | Extension | Status | What it does | +| --- | --- | --- | --- | +| [`ghl-workflow-tools`](./ghl-workflow-tools/) | GHL Workflow Backup | Available | Exports every workflow in the current sub-account as re-importable JSON in a ZIP, and restores workflows from a backup — as new drafts or in-place overwrites | +| `ghl-*` — pipelines, calendars, custom values, templates, funnels, forms, and the rest of the account | — | Planned | One focused tool per facet; see [Roadmap](#roadmap) | ## Why it exists @@ -29,7 +31,7 @@ Bulk export tools that exist are all-or-nothing: they assume you want everything Every tool in the set follows the same contract, so behavior learned on one transfers to all of them: - **No credential handling.** Tools borrow the page's own authenticated HTTP client (`window.SHELL_STORE.$http`) inside the tab you are signed into. No token is read, stored, or transmitted by the extension. -- **Read-only.** Every request a tool makes is a GET. Nothing in the sub-account is modified. +- **Exports are read-only.** Every request an export makes is a GET. Writes exist in exactly one place today — the workflow tool's Import tab — where they are always explicit, user-initiated actions: restores land as new **drafts** by default, overwriting a published workflow requires an extra acknowledgement, and nothing is ever published for you. - **Minimal permissions.** `activeTab`, `scripting`, `downloads`. No host permissions, no background service worker, no remote code. - **Works on any GHL host.** White-labelled domains included — tools never hardcode `app.gohighlevel.com`. - **Deterministic output.** Sorted keys, fixed timestamps, and volatile fields (signed URLs, per-user permission metadata) stripped, so an unchanged sub-account re-exports byte-identically and `git status` stays quiet. @@ -40,7 +42,7 @@ About a minute, no build step: 1. Open `chrome://extensions`. 2. Turn on **Developer mode** (top right). -3. **Load unpacked** → select the `ghl-workflow-exporter` folder from your clone of this repo. +3. **Load unpacked** → select the `ghl-workflow-tools` folder from your clone of this repo. 4. Open a HighLevel sub-account tab, click the extension icon, hit **Export workflows**. ## Install @@ -56,13 +58,13 @@ Then load the folder of the tool you want via `chrome://extensions` → **Load u ## Usage -Each tool has its own README with its exact flow — start with [`ghl-workflow-exporter/README.md`](./ghl-workflow-exporter/README.md). The shared pattern across all of them: +Each tool has its own README with its exact flow — start with [`ghl-workflow-tools/README.md`](./ghl-workflow-tools/README.md). The shared pattern across all of them: 1. Open the sub-account tab you care about (the tool shows which sub-account it detected). 2. Click the tool's icon and run its action. -3. A ZIP lands in your downloads, shaped for unzipping straight into a git repo. +3. An export lands as a ZIP in your downloads, shaped for unzipping straight into a git repo; a restore takes a previous export (`.zip` or loose `.json`) and writes it back — as drafts unless you explicitly overwrite. -The typical workflow-export session produces: +A workflow backup session produces: ```text legendary-academy-/ @@ -104,9 +106,10 @@ flowchart LR A -->|deterministic ZIP
zip.js| E[Download] ``` -- `popup.js` — orchestration: probes the tab, drives the export loop, builds files. +- `popup.js` — orchestration: probes the tab, drives the export and import loops, builds files. - `agent.js` — functions injected into the page's MAIN world. They must be fully self-contained (no imports, no closures) because they are serialized and re-parsed in the page. - `zip.js` — dependency-free ZIP writer with a fixed timestamp, so identical content produces an identical archive. +- `unzip.js` — dependency-free ZIP reader (the browser's own `DecompressionStream`), so backups can be read back without third-party code. ## Development @@ -137,3 +140,5 @@ PRs welcome, especially for the roadmap facets above — one tool per PR, follow ## License GHL Toolset is licensed under the [GNU Affero General Public License v3.0](./LICENSE). Each tool in the set carries the same license. + +Copyright (c) 2026 [Legion Code Inc.](https://github.com/legioncodeinc) diff --git a/ghl-workflow-tools/README.md b/ghl-workflow-tools/README.md index 7fd2216..0348be2 100644 --- a/ghl-workflow-tools/README.md +++ b/ghl-workflow-tools/README.md @@ -133,3 +133,7 @@ Re-exporting an unchanged workflow produces a byte-identical file, so ### 1.0.0 - Export every workflow in the current sub-account as a ZIP. + +--- + +By [Legion Code Inc.](https://github.com/legioncodeinc) · part of the [GHL Toolset](https://github.com/legioncodeinc/ghl-toolset) · AGPL-3.0 diff --git a/ghl-workflow-tools/manifest.json b/ghl-workflow-tools/manifest.json index c0c77ad..ea1f9c0 100644 --- a/ghl-workflow-tools/manifest.json +++ b/ghl-workflow-tools/manifest.json @@ -3,6 +3,8 @@ "name": "GHL Workflow Backup", "version": "1.1.0", "description": "Exports every workflow in the HighLevel sub-account you are viewing as re-importable JSON, and restores them from a backup file.", + "author": "Legion Code Inc.", + "homepage_url": "https://github.com/legioncodeinc/ghl-toolset", "permissions": [ "activeTab", "scripting", diff --git a/ghl-workflow-tools/popup.html b/ghl-workflow-tools/popup.html index 22c629a..cf5a460 100644 --- a/ghl-workflow-tools/popup.html +++ b/ghl-workflow-tools/popup.html @@ -2,12 +2,12 @@ -GHL Workflow Exporter +GHL Workflow Backup
-

Workflow Exporter

+

Workflow Backup

Checking this tab…

@@ -58,5 +58,6 @@

Workflow Exporter

+

by Legion Code Inc.