fix(deploy): prevent frontend patch failure and pre-bundle patches
All checks were successful
Deploy Plane Project Tracker / Deploy to cisystem (push) Successful in 55s

- Check local disk first in patch_frontend.py to avoid remote HTTP retrieval
- Use robust streaming with timeout and user-agent fallback instead of urlretrieve
- Include pre-patched frontend JS files in deploy/patches
- Add safe fallback in install.sh so optional patch generation does not fail deployment
This commit is contained in:
vickytechkey 2026-11-06 17:38:49 +05:30
parent e67f0cf15d
commit b0e37091d4
5 changed files with 150 additions and 42 deletions

View file

@ -79,7 +79,7 @@ docker compose run --rm migrator
echo "==> Generating frontend Enterprise patches..."
if [ -f "${INSTALL_DIR}/patches/patch_frontend.py" ]; then
python3 "${INSTALL_DIR}/patches/patch_frontend.py" "${INSTALL_DIR}/patches"
python3 "${INSTALL_DIR}/patches/patch_frontend.py" "${INSTALL_DIR}/patches" || true
fi
echo "==> [5/6] Starting Plane containers without touching other host services..."

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,74 @@
import{F as e,V as t,j as n}from"./chunk-KS7C4IRE-CMpS5gbI.js";import{t as r}from"./jsx-runtime-noaePsda.js";import{Br as i,J as a,Ti as o,c as s,dr as c,lr as l,vt as u,yt as d}from"./issue.service-CTC2rMYd.js";import{I as f,V as p,Vt as m,jn as h}from"./store-context-C83lXiWw.js";import{a as g}from"./dist-B3GnvwXu.js";import{a as _,i as v,n as y,r as b,t as x}from"./minus-BpQOnDpY.js";import{t as S}from"./item-icon-Cbeg99eb.js";import{t as C}from"./circle-check-ixOmpmsy.js";import{t as w}from"./message-square-C_OV-WER.js";import{t as T}from"./button-DrwsAH69-BTPMslkc.js";import"./button-BYpq06q8.js";import{t as E}from"./mobxreact.esm-CAsEUKPK.js";import{t as D}from"./page-title-CHr-UyVa.js";import{t as O}from"./use-workspace-Djnbfdo5.js";import{t as k}from"./user-NVe3rMCN.js";import{t as A}from"./boxed-control-item-CaQeQjDm.js";import{t as j}from"./heading-NrAC1MPz.js";import{t as M}from"./discount-info-t73U_blP.js";import{t as N}from"./breadcrumb-link-XZ0a1zCq.js";import{t as P}from"./not-authorized-view-BL2zvqmA.js";import{n as F,t as I}from"./page-header-5H3k3FUd.js";var L=r(),R=E(function(){let{t:e}=s(),t=i[`billing-and-plans`],n=S[`billing-and-plans`];return(0,L.jsx)(I,{leftItem:(0,L.jsx)(`div`,{className:`flex items-center gap-2`,children:(0,L.jsx)(g,{children:(0,L.jsx)(g.Item,{component:(0,L.jsx)(N,{label:e(t.i18n_label),icon:(0,L.jsx)(n,{className:`size-4 text-tertiary`})})})})})})}),z=t(e(),1);function B({className:e}){return(0,L.jsx)(w,{className:p(e,`size-5 text-secondary`)})}function V({className:e}){return(0,L.jsx)(`span`,{className:p(`w-fit rounded-sm bg-accent-primary px-1.5 py-0.5 text-9 font-semibold whitespace-nowrap text-on-color`,e),children:`COMING SOON`})}var H=[`free`,`one`,`pro`,`business`,`enterprise`],U=[{id:`project-work-tracking`,title:`Project + work tracking`,features:[{title:`Projects`,description:`Add projects to house work items, cycles, and modules.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Work items`,description:`Add work via work items, set properties for tracking, and add to
cycles or modules.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Comments`,description:`Respond to work items, @mention members, and brainstorm
together without leaving Plane.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Cycles`,description:`Track work in timeboxes with differing frequency.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Modules`,description:`Group replicable work in modules with their own
leads.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Intake`,description:`See suggestions and feedback from viewers and
guests before you decide to add them to your
project.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Estimates`,description:`Measure effort in points in a system that works for
you.`,cloud:{free:`Basic`,one:`Basic`,pro:`Advanced`,business:`Advanced`,enterprise:`Advanced`}}]},{id:`project-work-management`,title:`Project + work management`,features:[{title:`Bulk Ops`,description:`Add several work items to cycles or modules, transfer
them, or edit their properties.`,cloud:{free:!1,one:`Limited props`,pro:`All props`,business:(0,L.jsxs)(`span`,{className:`flex flex-col items-end gap-1 lg:items-center`,children:[(0,L.jsx)(V,{}),`Work item transfers and conversions`]}),enterprise:(0,L.jsxs)(`span`,{className:`flex flex-col items-end gap-1 lg:items-center`,children:[(0,L.jsx)(V,{}),`Work item transfers and conversions`]})}},{title:`Time Tracking + Worklogs`,description:`Track time per work item, see aggregated reports, and
filter by need.`,cloud:{free:!1,one:`Basic`,pro:`Historical timesheets`,business:`Historical timesheets
and approvals`,enterprise:`Historical timesheets
and approvals`}},{title:`Active Cycles`,description:`See all running cycles across all projects, or soon, in
a single project.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Work item Types`,description:`Create your own work item types with your own
properties.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Custom Properties`,description:`Create your own properties and apply them to your
workspace or project.`,cloud:{free:!1,one:!1,pro:`Project-level
custom properties`,business:`Workspace-level
properties and roll-ups`,enterprise:`Workspace-level
properties and roll-ups`}},{title:`Dependencies in Gantt`,description:`Adjust timelines for dependent work items visually on
our Gantt layout.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Work item Transfers`,description:`Move a work item from a project or a cycle to
another.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Auto-transfer Cycle Work items`,description:`Transfer incomplete work items from a completed cycle
to the next cycle or to the default project state. `,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Epics`,description:`Organize long-term work in epics that house work items,
cycles, and modules.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Initiatives`,description:`Create initiatives to roll up several epics.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Checkpoints`,description:`Add markers to Projects, Epics and Initiatives to keep your
team on track and report on progress.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Module Overview`,description:`Like Cycle Overviews, see relevant details and
progress charts for each module.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Auto-assignment In Modules`,description:`Choose assignment rules for work items in a
module including Linear, Round Robin, or Capacity.`,cloud:{free:!1,one:!1,pro:`Linear`,business:`Round-robin and Capacity`,enterprise:`Round-robin and Capacity`}},{title:`Public, Private, and Secret projects`,description:`Public projects are visible and accessible to
everyone. Private ones are visible but need approval
to join. Secret projects aren't visible or accessible.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`State Of Projects`,description:`See all projects laid across states that highlight
those that need attention and those on track.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Pre-defined work item Templates`,description:`Choose from our available work item templates that
customize work item types and properties for several
use cases.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Teamspace Cycles`,description:`See multiple cycles in multiple projects at once.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Project Templates`,description:`Save states, workflows, automation, and other project
settings into templates.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Baselines And Deviations`,description:`Declare baselines for how your projects progress
and zoom in on deviations.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Scheduled Comms`,description:`Schedule reports, notifications, and messages to
third-party tools.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Intake Assignees`,description:`Assign approved Intake work items to a member by
default.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Custom SLAs`,description:`Set SLA matrices for time-sensitive work items.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Intake Forms`,description:`Take Intake work items from externally accessible web
forms.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Emails For Intake`,description:`Get an email address for reporting work items
directly into a project's Intake.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}}]},{id:`visualization`,title:`Visualization`,features:[{title:`Layouts`,description:`Choose from the List, the Board, the Calendar, the
Gantt, or the Spreadsheet layout for your work items.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Views`,description:`Save sort, filter, and display options on a layout to a
view.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Shared Views`,description:`Choose a few members to share a view with.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Publish Views`,description:`Put a view on the Internet and let your customers
interact with them.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Dashboards and Widgets`,description:`Create your own dashboards with custom widgets
and data types.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}}]},{id:`analytics-reports`,title:`Analytics + reports`,features:[{title:`Progress Charts`,description:`Track progress in cycles, modules, and overviews
throughout Plane without switching to dashboards
or Analytics.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Cycle Reports`,description:`Get on-demand cycle reports during and after a
cycle. Revisit reports anytime from permalinks.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Insights`,description:`Hindsight, On-demand insights, Foresights.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Advanced Pages Analytics`,description:`See who's viewing, sharing, and commenting on
your pages along with other useful info.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Custom Reports`,description:`Generate reports by any dimension and metric
across your project or workspace.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}}]},{id:`navigation`,title:`Navigation`,features:[{title:`Power K`,description:`Access a keyboard-first gateway to almost anything
in Plane.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`PQL`,description:`Write Plane Query Language in search with support
for Boolean operators. Soon, you can write natural
language queries.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}}]},{id:`workspace-user-management`,title:`Workspace and user management`,features:[{title:`Member limit`,description:`Number of seats that can use project and work management features`,selfHostedDescription:`Number of users that our standard infra supports
Increase infra to get more users`,cloud:{free:`12`,one:``,pro:`Unlimited`,business:`Unlimited`,enterprise:`Unlimited`},"self-hosted":{free:`~50`,one:`~50`,pro:`~200`,business:`~200`,enterprise:`Unlimited`}},{title:`Roles`,description:`Choose from one of four pre-defined roles or create
custom ones with RBAC.`,cloud:{free:`Basic`,one:`Basic`,pro:`Pre-defined roles`,business:`RBAC`,enterprise:`GAC`}},{title:`Guests`,description:`Let some users see everything or just their work items in
a project.`,cloud:{free:!1,one:`5 per paid member`,pro:`5 per paid member`,business:`5 per paid member`,enterprise:`5 per paid member`}},{title:`Approvals`,description:`Set workspace, project, and work item type approvals to
designated admins.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Admin Interface`,description:`Get an admin overview to manage workspace and
project settings.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Workspace Activity Logs`,description:`See filterable activity logs for your entire
workspace.`,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`API-enabled Audit Logs`,description:`See a full-workspace audit log and use APIs to flag
Plane activity in compliance systems.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}}]},{id:`automations-workflows`,title:`Automations and workflows`,features:[{title:`Trigger And Action`,description:`Choose a trigger and a corresponding action per
automation flow.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Decisions And Loops Automation`,description:`Use actions as triggers indefinitely in an
automation flow.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Number of automations`,description:`Total number of automation flows in your
workspace`,cloud:{free:!1,one:!1,pro:`5,000`,business:`10,000`,enterprise:`Unlimited`}}]},{id:`knowledge-management`,title:`Knowledge management`,features:[{title:`Pages`,description:`Build knowledge bases for your teams which are
accessible & shareable.`,cloud:{free:!0,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Real-time Collab`,description:`Edit a page together with members in your project,
team, or workspace.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Work item Embeds`,description:`Embed work items from any project you are a member
of.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Link-to-work items`,description:`Link pages in work items in a separate section in work item
details.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Publish`,description:`Put your pages on the web for external users and let
them comment without signing into your workspace.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Wiki`,description:`Create company-wide wikis or knowledge bases
without creating a project.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`Exports`,description:`Export page content into PDFs or Word-compatible
docs.`,cloud:{free:!1,one:!1,pro:`One download
at a time`,business:`Queued downloads`,enterprise:`Queued downloads`}},{title:`Templates`,description:`Use pages as templates for your project, team, or
workspace.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Versions`,description:`See restorable version of edits to your pages.`,cloud:{free:!1,one:!1,pro:`2 days`,business:`3 months`,enterprise:`Unlimited`}},{title:`Databases + Formulas`,description:`Put databases and formulas into a page without
worrying about losing text, images, or other content
types.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:!0,enterprise:!0}},{title:`Nested Pages`,description:`Pages inside a page, organize your pages
as you see fit for the progressive
disclosure.`,comingSoon:!0,cloud:{free:!1,one:!1,pro:!1,business:`Word-compatible + other format downloads`,enterprise:`Word-compatible + other format downloads`}}]},{id:`importers`,title:`Importers`,features:[{title:`Jira`,description:`Import your work items and members from Jira.`,cloud:{free:`Without custom props`,one:`Without custom props`,pro:`With custom props`,business:`With custom props`,enterprise:`With custom props`}},{title:`GitHub`,description:`Import your work items and members from GitHub.`,cloud:{free:`Without custom props`,one:`Without custom props`,pro:`With custom props`,business:`With custom props`,enterprise:`With custom props`}}]},{id:`integrations`,title:`Integrations`,comingSoon:!0,features:[{title:`GitHub`,description:`Sync Plane work items and states to GitHub work items and
states. Update GitHub automatically with activity
from Plane and vice-versa.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Slack`,description:`Get Plane activity in Slack and use / commands in
Slack to make changes in Plane.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Zapier`,description:`Run if-then-else automations using Zapier.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Zendesk`,description:`Create Plane work items from Zendesk tickets.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}},{title:`Freshdesk`,description:`Create Plane work items from Freshdesk tickets.`,cloud:{free:!1,one:!1,pro:!0,business:!0,enterprise:!0}}]},{id:`storage`,title:`Storage`,cloudOnly:!0,features:[{title:`Space`,description:`Total storage allowed per workspace`,cloud:{free:`5GB`,one:!1,pro:`1 TB`,business:`5 TB`,enterprise:`Custom`}},{title:`Max file size`,description:`Limit for uploads to your workspace`,cloud:{free:`5 MB`,one:!1,pro:`100 MB`,business:`200 MB`,enterprise:`Custom`}}]},{id:`security`,title:`Security`,features:[{title:`SAML`,description:`Get the officially supported SAML implementation
and make Plane secure with any IdP.`,cloud:{free:!1,one:!0,pro:!0,business:!0,enterprise:!0}},{title:`OIDC`,description:`Get the officiall

View file

@ -5,6 +5,10 @@ import sys
import urllib.request
def extract_or_fetch(filename, out_path, image="makeplane/plane-frontend:stable"):
if os.path.exists(out_path) and os.path.getsize(out_path) > 50:
print(f"Using existing {filename} on host ({os.path.getsize(out_path)} bytes)")
return True
for base_dir in ["/usr/share/caddy/html/assets", "/usr/share/nginx/html/assets"]:
src_container_path = f"{base_dir}/{filename}"
try:
@ -18,64 +22,92 @@ def extract_or_fetch(filename, out_path, image="makeplane/plane-frontend:stable"
with open(out_path, "wb") as f:
f.write(res.stdout)
print(f"Extracted {filename} from {image} ({len(res.stdout)} bytes)")
return
return True
except Exception:
continue
url = f"https://jira.tradhox.com/assets/{filename}"
urllib.request.urlretrieve(url, out_path)
print(f"Fetched {filename} from {url}")
try:
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"})
with urllib.request.urlopen(req, timeout=15) as resp:
content = resp.read()
if len(content) > 50:
with open(out_path, "wb") as f:
f.write(content)
print(f"Fetched {filename} from {url} ({len(content)} bytes)")
return True
except Exception as e:
print(f"Notice: Could not fetch {filename} from {url}: {e}")
# Fallback stub to satisfy docker-compose volume mounts if needed
with open(out_path, "w", encoding="utf-8") as f:
f.write("// frontend patch stub\n")
print(f"Created fallback stub for {filename}")
return False
def patch_page(file_path):
with open(file_path, "r", encoding="utf-8") as f:
code = f.read()
if not os.path.exists(file_path):
return
try:
with open(file_path, "r", encoding="utf-8") as f:
code = f.read()
# 1. Replace Community with Enterprise in top box
old_box = "(0,L.jsx)(A,{title:`Community`,description:`Unlimited projects, issues, cycles, modules, pages, and storage`})"
new_box = "(0,L.jsx)(A,{title:`Enterprise`,description:`Enterprise Edition Active — Unlimited users, projects, issues, cycles, modules, pages, storage, and all enterprise features unlocked`})"
if old_box in code:
code = code.replace(old_box, new_box)
# 1. Replace Community with Enterprise in top box
old_box = "(0,L.jsx)(A,{title:`Community`,description:`Unlimited projects, issues, cycles, modules, pages, and storage`})"
new_box = "(0,L.jsx)(A,{title:`Enterprise`,description:`Enterprise Edition Active — Unlimited users, projects, issues, cycles, modules, pages, storage, and all enterprise features unlocked`})"
if old_box in code:
code = code.replace(old_box, new_box)
# 2. Update W for enterprise
old_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`billed monthly`,yearlyPriceSecondaryDescription:`billed yearly`,buttonCTA:`Talk to Sales`,isActive:!1}"
new_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`Active Plan`,yearlyPriceSecondaryDescription:`Active Plan`,buttonCTA:`Active Plan`,isActive:!0}"
if old_ent in code:
code = code.replace(old_ent, new_ent)
# 2. Update W for enterprise
old_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`billed monthly`,yearlyPriceSecondaryDescription:`billed yearly`,buttonCTA:`Talk to Sales`,isActive:!1}"
new_ent = "enterprise:{id:o.ENTERPRISE,name:`Enterprise`,monthlyPriceSecondaryDescription:`Active Plan`,yearlyPriceSecondaryDescription:`Active Plan`,buttonCTA:`Active Plan`,isActive:!0}"
if old_ent in code:
code = code.replace(old_ent, new_ent)
# 3. Update Y pricing descriptions and button
old_ph = "p=d?`a user per month`:`Quote on request`,h=r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
new_ph = "p=t===o.ENTERPRISE?`Active Lifetime License`:d?`a user per month`:`Quote on request`,h=t===o.ENTERPRISE?`All features unlocked`:r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
if old_ph in code:
code = code.replace(old_ph, new_ph)
# 3. Update Y pricing descriptions and button
old_ph = "p=d?`a user per month`:`Quote on request`,h=r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
new_ph = "p=t===o.ENTERPRISE?`Active Lifetime License`:d?`a user per month`:`Quote on request`,h=t===o.ENTERPRISE?`All features unlocked`:r===`month`?n.monthlyPriceSecondaryDescription:n.yearlyPriceSecondaryDescription;"
if old_ph in code:
code = code.replace(old_ph, new_ph)
old_btn = "(0,L.jsx)(T,{variant:`primary`,size:`lg`,onClick:()=>{let e=r??`year`,n=l[t][e]??`https://plane.so/talk-to-sales`;window.open(n,`_blank`)},className:`w-full`,children:d?`Upgrade to ${u}`:a(`common.upgrade_cta.talk_to_sales`)})"
new_btn = "(0,L.jsx)(T,{variant:t===o.ENTERPRISE?`primary`:`secondary`,size:`lg`,disabled:!0,className:t===o.ENTERPRISE?`w-full opacity-95 cursor-default`:`w-full opacity-60 cursor-default`,children:t===o.ENTERPRISE?`Current Plan (Active)`:`Included in Enterprise`})"
if old_btn in code:
code = code.replace(old_btn, new_btn)
old_btn = "(0,L.jsx)(T,{variant:`primary`,size:`lg`,onClick:()=>{let e=r??`year`,n=l[t][e]??`https://plane.so/talk-to-sales`;window.open(n,`_blank`)},className:`w-full`,children:d?`Upgrade to ${u}`:a(`common.upgrade_cta.talk_to_sales`)})"
new_btn = "(0,L.jsx)(T,{variant:t===o.ENTERPRISE?`primary`:`secondary`,size:`lg`,disabled:!0,className:t===o.ENTERPRISE?`w-full opacity-95 cursor-default`:`w-full opacity-60 cursor-default`,children:t===o.ENTERPRISE?`Current Plan (Active)`:`Included in Enterprise`})"
if old_btn in code:
code = code.replace(old_btn, new_btn)
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
print(f"Patched page successfully: {file_path} ({len(code)} bytes)")
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
print(f"Patched page successfully: {file_path} ({len(code)} bytes)")
except Exception as e:
print(f"Notice: Could not patch page {file_path}: {e}")
def patch_layout(file_path):
with open(file_path, "r", encoding="utf-8") as f:
code = f.read()
if not os.path.exists(file_path):
return
try:
with open(file_path, "r", encoding="utf-8") as f:
code = f.read()
old_badge = 'onClick:()=>t(!0),"aria-haspopup":`dialog`,"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Community`'
new_badge = 'onClick:()=>{},"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Enterprise`'
if old_badge in code:
code = code.replace(old_badge, new_badge)
old_badge = 'onClick:()=>t(!0),"aria-haspopup":`dialog`,"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Community`'
new_badge = 'onClick:()=>{},"aria-label":n(`aria_labels.projects_sidebar.edition_badge`),children:`Enterprise`'
if old_badge in code:
code = code.replace(old_badge, new_badge)
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
print(f"Patched layout successfully: {file_path} ({len(code)} bytes)")
with open(file_path, "w", encoding="utf-8") as f:
f.write(code)
print(f"Patched layout successfully: {file_path} ({len(code)} bytes)")
except Exception as e:
print(f"Notice: Could not patch layout {file_path}: {e}")
def patch_badge(file_path):
# The upgrade badge returns null so no "Pro" badges render anywhere
new_code = 'import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};\n'
with open(file_path, "w", encoding="utf-8") as f:
f.write(new_code)
print(f"Patched badge successfully: {file_path} ({len(new_code)} bytes)")
try:
# The upgrade badge returns null so no "Pro" badges render anywhere
new_code = 'import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};\n'
with open(file_path, "w", encoding="utf-8") as f:
f.write(new_code)
print(f"Patched badge successfully: {file_path} ({len(new_code)} bytes)")
except Exception as e:
print(f"Notice: Could not patch badge {file_path}: {e}")
def main():
target_dir = sys.argv[1] if len(sys.argv) > 1 else os.path.dirname(os.path.abspath(__file__))

View file

@ -0,0 +1 @@
import{t as e}from"./jsx-runtime-noaePsda.js";var r=e();function i(e){return null;}export{i as t};