studiograph 1.3.48-next.141 → 1.3.48-next.142
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/README.md +13 -9
- package/dist/agent/skills/interview/skill.md +0 -15
- package/dist/agent/tools/ops-tools.js +30 -23
- package/dist/agent/tools/ops-tools.js.map +1 -1
- package/dist/cli/commands/admin/audit-workspace.d.ts +23 -0
- package/dist/cli/commands/admin/audit-workspace.js +133 -0
- package/dist/cli/commands/admin/audit-workspace.js.map +1 -0
- package/dist/cli/commands/admin/audit.d.ts +21 -0
- package/dist/cli/commands/admin/audit.js +174 -0
- package/dist/cli/commands/admin/audit.js.map +1 -0
- package/dist/cli/commands/admin/backup.d.ts +54 -0
- package/dist/cli/commands/admin/backup.js +207 -0
- package/dist/cli/commands/admin/backup.js.map +1 -0
- package/dist/cli/commands/admin/folder.d.ts +11 -0
- package/dist/cli/commands/admin/folder.js +186 -0
- package/dist/cli/commands/admin/folder.js.map +1 -0
- package/dist/cli/commands/admin/index.d.ts +16 -0
- package/dist/cli/commands/admin/index.js +44 -0
- package/dist/cli/commands/admin/index.js.map +1 -0
- package/dist/cli/commands/admin/migrate.d.ts +56 -0
- package/dist/cli/commands/admin/migrate.js +263 -0
- package/dist/cli/commands/admin/migrate.js.map +1 -0
- package/dist/cli/commands/admin/restore.d.ts +24 -0
- package/dist/cli/commands/admin/restore.js +228 -0
- package/dist/cli/commands/admin/restore.js.map +1 -0
- package/dist/cli/commands/admin/secrets.d.ts +23 -0
- package/dist/cli/commands/admin/secrets.js +256 -0
- package/dist/cli/commands/admin/secrets.js.map +1 -0
- package/dist/cli/commands/admin/settings.d.ts +28 -0
- package/dist/cli/commands/admin/settings.js +284 -0
- package/dist/cli/commands/admin/settings.js.map +1 -0
- package/dist/cli/commands/admin/token.d.ts +42 -0
- package/dist/cli/commands/admin/token.js +126 -0
- package/dist/cli/commands/admin/token.js.map +1 -0
- package/dist/cli/commands/admin/transfer-ownership.d.ts +15 -0
- package/dist/cli/commands/admin/transfer-ownership.js +106 -0
- package/dist/cli/commands/admin/transfer-ownership.js.map +1 -0
- package/dist/cli/commands/admin/user.d.ts +11 -0
- package/dist/cli/commands/admin/user.js +187 -0
- package/dist/cli/commands/admin/user.js.map +1 -0
- package/dist/cli/commands/clone.js +22 -3
- package/dist/cli/commands/clone.js.map +1 -1
- package/dist/cli/commands/config.d.ts +23 -107
- package/dist/cli/commands/config.js +52 -260
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/connector.d.ts +10 -13
- package/dist/cli/commands/connector.js +16 -58
- package/dist/cli/commands/connector.js.map +1 -1
- package/dist/cli/commands/deploy.js +17 -17
- package/dist/cli/commands/deploy.js.map +1 -1
- package/dist/cli/commands/init.js +3 -25
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/redeploy.js +23 -10
- package/dist/cli/commands/redeploy.js.map +1 -1
- package/dist/cli/commands/reset.d.ts +22 -6
- package/dist/cli/commands/reset.js +132 -34
- package/dist/cli/commands/reset.js.map +1 -1
- package/dist/cli/commands/serve.js +20 -0
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/sync.js +66 -297
- package/dist/cli/commands/sync.js.map +1 -1
- package/dist/cli/crypto-bundle.d.ts +39 -0
- package/dist/cli/crypto-bundle.js +94 -0
- package/dist/cli/crypto-bundle.js.map +1 -0
- package/dist/cli/index.js +22 -30
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/railway-pin.d.ts +68 -0
- package/dist/cli/railway-pin.js +96 -0
- package/dist/cli/railway-pin.js.map +1 -0
- package/dist/cli/setup-wizard.js +4 -2
- package/dist/cli/setup-wizard.js.map +1 -1
- package/dist/core/graph.d.ts +18 -0
- package/dist/core/graph.js +41 -5
- package/dist/core/graph.js.map +1 -1
- package/dist/core/secrets/registry.js +15 -0
- package/dist/core/secrets/registry.js.map +1 -1
- package/dist/core/user-config.d.ts +23 -0
- package/dist/core/user-config.js +36 -0
- package/dist/core/user-config.js.map +1 -1
- package/dist/core/workspace-manager.d.ts +3 -0
- package/dist/core/workspace-manager.js +10 -1
- package/dist/core/workspace-manager.js.map +1 -1
- package/dist/server/routes/auth-api.js +33 -10
- package/dist/server/routes/auth-api.js.map +1 -1
- package/dist/server/routes/git-http.js +4 -4
- package/dist/server/routes/git-http.js.map +1 -1
- package/dist/server/routes/graph-api.js +23 -1
- package/dist/server/routes/graph-api.js.map +1 -1
- package/dist/server/routes/insights-api.js +8 -41
- package/dist/server/routes/insights-api.js.map +1 -1
- package/dist/services/vector-service.d.ts +61 -2
- package/dist/services/vector-service.js +137 -3
- package/dist/services/vector-service.js.map +1 -1
- package/dist/utils/version-checker.d.ts +12 -22
- package/dist/utils/version-checker.js +14 -104
- package/dist/utils/version-checker.js.map +1 -1
- package/dist/web/_app/immutable/assets/{0.m6KHz5Ct.css → 0.rk06bb50.css} +1 -1
- package/dist/web/_app/immutable/assets/CalendarView.2_5KwEnm.css +1 -0
- package/dist/web/_app/immutable/assets/{Filters.DtCty8E-.css → Filters.DW9xqrSh.css} +1 -1
- package/dist/web/_app/immutable/assets/{KanbanView.G3NZ0uHb.css → KanbanView.BykCv6-6.css} +1 -1
- package/dist/web/_app/immutable/assets/{TimelineView.CTJIfMlQ.css → TimelineView.D5Vr4f4p.css} +1 -1
- package/dist/web/_app/immutable/chunks/{DrKGybIx.js → -6gQBbFR.js} +1 -1
- package/dist/web/_app/immutable/chunks/{hkz4tVDj.js → 0DopuZap.js} +1 -1
- package/dist/web/_app/immutable/chunks/{C6VKh19i2.js → 1-LQKXfZ2.js} +1 -1
- package/dist/web/_app/immutable/chunks/25df8L_v2.js +1 -0
- package/dist/web/_app/immutable/chunks/7DYGAJq32.js +1 -0
- package/dist/web/_app/immutable/chunks/{DoqF9D6X.js → B49iyw-B.js} +1 -1
- package/dist/web/_app/immutable/chunks/B8WVxDoi.js +1 -0
- package/dist/web/_app/immutable/chunks/{CMTVWAWD2.js → B8ern2gu2.js} +1 -1
- package/dist/web/_app/immutable/chunks/BL_MJFAi2.js +1 -0
- package/dist/web/_app/immutable/chunks/{BPLOxwd9.js → BQkTMTc1.js} +1 -1
- package/dist/web/_app/immutable/chunks/BRYgZsoN.js +1 -0
- package/dist/web/_app/immutable/chunks/{Brmm2B1F.js → Ba4GBqJu.js} +3 -3
- package/dist/web/_app/immutable/chunks/BbvKJFF_.js +1 -0
- package/dist/web/_app/immutable/chunks/{DFI49Ojd.js → Beinnj2F.js} +1 -1
- package/dist/web/_app/immutable/chunks/{3dvegJyw.js → BfYCB5cu.js} +4 -4
- package/dist/web/_app/immutable/chunks/{Bx_sUbFW2.js → Bgd45X5W2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{B0sOCB6J2.js → Bn5UWofR2.js} +2 -2
- package/dist/web/_app/immutable/chunks/BnRO5LhI.js +1 -0
- package/dist/web/_app/immutable/chunks/Bq0v_R5r.js +2 -0
- package/dist/web/_app/immutable/chunks/{uYkLLAve.js → BrVXfxYL.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BNMvG3vo.js → BzHJlS8U.js} +1 -1
- package/dist/web/_app/immutable/chunks/{D_x_Qx4b.js → C0r3tbe6.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DWAwVvou2.js → C2rrtlqJ2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{Cpzw6IR0.js → C6wTxDUN.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DFa-r0hK2.js → C758laSC2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BsRJ3k1d.js → C7dZPzrF.js} +1 -1
- package/dist/web/_app/immutable/chunks/C7ehUIgK.js +1 -0
- package/dist/web/_app/immutable/chunks/{DimvZ1rk.js → CDdrTEPh.js} +1 -1
- package/dist/web/_app/immutable/chunks/{1lBMH6iW2.js → CDjqTDPq.js} +1 -1
- package/dist/web/_app/immutable/chunks/{H2kmhAjb2.js → CEzdXAzx.js} +1 -1
- package/dist/web/_app/immutable/chunks/CKyVp7pa.js +1 -0
- package/dist/web/_app/immutable/chunks/{CZl1ot9W.js → CNDUnAll.js} +1 -1
- package/dist/web/_app/immutable/chunks/{CQmPiyUp.js → CQZjBhsQ.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BL1oxZiW2.js → CQpwu0Rw.js} +1 -1
- package/dist/web/_app/immutable/chunks/Ca4KscMv2.js +1 -0
- package/dist/web/_app/immutable/chunks/{DnvUa5lw2.js → CeH-DnOb2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DGzcahbp.js → Cl7sUEPM.js} +1 -1
- package/dist/web/_app/immutable/chunks/{D3fuCoSW2.js → ClZaQQEO2.js} +1 -1
- package/dist/web/_app/immutable/chunks/CqpDNs5Z.js +1 -0
- package/dist/web/_app/immutable/chunks/{Ds-56rNC.js → Cspt7VYo.js} +1 -1
- package/dist/web/_app/immutable/chunks/{C_SyfpYe.js → CvnXl5Rr.js} +1 -1
- package/dist/web/_app/immutable/chunks/{D5hsPE8N.js → Cw3xz8bO.js} +1 -1
- package/dist/web/_app/immutable/chunks/{Dz1qECzz.js → Cx1nIj6x.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DuZvkPqs2.js → CyiNWqN72.js} +1 -1
- package/dist/web/_app/immutable/chunks/Cz2FjHF6.js +1 -0
- package/dist/web/_app/immutable/chunks/{DfLl5AcG2.js → Czwjp7ip2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DKVEBJ-32.js → D5YwRLyY2.js} +1 -1
- package/dist/web/_app/immutable/chunks/D5ydJQMZ.js +1 -0
- package/dist/web/_app/immutable/chunks/{iyvdeKmj.js → D9FQivYK.js} +1 -1
- package/dist/web/_app/immutable/chunks/{DOEenJe1.js → DARmfkV3.js} +1 -1
- package/dist/web/_app/immutable/chunks/DBANz5d9.js +1 -0
- package/dist/web/_app/immutable/chunks/DEsRD0Kx.js +1 -0
- package/dist/web/_app/immutable/chunks/{BqbXE992.js → DFGbrpeM.js} +3 -3
- package/dist/web/_app/immutable/chunks/DFKfEAag2.js +1 -0
- package/dist/web/_app/immutable/chunks/DGBLBaQ_.js +1 -0
- package/dist/web/_app/immutable/chunks/{CQ-2yccd.js → DJpbNaAK.js} +1 -1
- package/dist/web/_app/immutable/chunks/{yvIeeb_42.js → DNgAQxDP.js} +1 -1
- package/dist/web/_app/immutable/chunks/DPD8EOKr2.js +2 -0
- package/dist/web/_app/immutable/chunks/{DjuziFSq.js → DPYXG0kj.js} +1 -1
- package/dist/web/_app/immutable/chunks/{4pIH0_tT.js → DRPw0stG.js} +1 -1
- package/dist/web/_app/immutable/chunks/{BLhU-GJf.js → DR_cAG4D.js} +1 -1
- package/dist/web/_app/immutable/chunks/{xaGl2eMs.js → DTBwqHgG.js} +1 -1
- package/dist/web/_app/immutable/chunks/{CfK1GcGP2.js → DYvLXLU62.js} +1 -1
- package/dist/web/_app/immutable/chunks/{Qqt3Iwci2.js → DZHwUnTK.js} +1 -1
- package/dist/web/_app/immutable/chunks/{Byqqksev.js → DZV_JkdQ2.js} +1 -1
- package/dist/web/_app/immutable/chunks/{C83yuW3m2.js → Dha-MiFJ2.js} +1 -1
- package/dist/web/_app/immutable/chunks/DnawuswL2.js +1 -0
- package/dist/web/_app/immutable/chunks/{BdDPs-DM.js → Dux-JxEb.js} +1 -1
- package/dist/web/_app/immutable/chunks/F4YWa-kI.js +1 -0
- package/dist/web/_app/immutable/chunks/{Ci3J2iwp2.js → GbgQ0w_p2.js} +1 -1
- package/dist/web/_app/immutable/chunks/L1BSvKbu2.js +1 -0
- package/dist/web/_app/immutable/chunks/kW0emxZs.js +1 -0
- package/dist/web/_app/immutable/chunks/{DEcbx4K4.js → u2YWWW0D.js} +1 -1
- package/dist/web/_app/immutable/chunks/{CqX-MwhK.js → ud-80bN1.js} +1 -1
- package/dist/web/_app/immutable/chunks/wQ835Iqq2.js +83 -0
- package/dist/web/_app/immutable/entry/app.DcFury20.js +2 -0
- package/dist/web/_app/immutable/entry/start.31Egd-4X.js +1 -0
- package/dist/web/_app/immutable/nodes/{0.CITPEjbR.js → 0.C15TiBjR.js} +2 -2
- package/dist/web/_app/immutable/nodes/{1.DduJECtM.js → 1.DBqnCp5N.js} +1 -1
- package/dist/web/_app/immutable/nodes/{10.Cui_JZxb.js → 10.BoogCEfh.js} +1 -1
- package/dist/web/_app/immutable/nodes/{11.UeCoxbD-.js → 11.B5C95aiY.js} +1 -1
- package/dist/web/_app/immutable/nodes/{12.B9WJ7c9l.js → 12.Bo39gio2.js} +1 -1
- package/dist/web/_app/immutable/nodes/{13.DqgCQkJL.js → 13.DEPiNgiO.js} +1 -1
- package/dist/web/_app/immutable/nodes/2.Dqhur93F.js +106 -0
- package/dist/web/_app/immutable/nodes/{3.DZhsdFdZ.js → 3.DXrb2xtu.js} +1 -1
- package/dist/web/_app/immutable/nodes/4.BR9ghNP-.js +2 -0
- package/dist/web/_app/immutable/nodes/{5.CMsuIqT6.js → 5.B-uwmPkj.js} +1 -1
- package/dist/web/_app/immutable/nodes/{6.CkWVXmyL.js → 6.CtKXZ-rA.js} +1 -1
- package/dist/web/_app/immutable/nodes/{7.BkYENY24.js → 7.CQ40ZEvw.js} +1 -1
- package/dist/web/_app/immutable/nodes/{8.DwdTJOgA.js → 8.DHbQ0NZN.js} +1 -1
- package/dist/web/_app/immutable/nodes/{9.DKZ3Sgx1.js → 9.5fODFcxN.js} +1 -1
- package/dist/web/_app/version.json +1 -1
- package/dist/web/index.html +14 -14
- package/package.json +1 -1
- package/dist/web/_app/immutable/assets/CalendarView.B_hbOAbM.css +0 -1
- package/dist/web/_app/immutable/chunks/-ioccU5s.js +0 -2
- package/dist/web/_app/immutable/chunks/BK0ggYL62.js +0 -1
- package/dist/web/_app/immutable/chunks/CA4g7cjE2.js +0 -1
- package/dist/web/_app/immutable/chunks/CF8PYvJA.js +0 -1
- package/dist/web/_app/immutable/chunks/CGKzi17I2.js +0 -1
- package/dist/web/_app/immutable/chunks/ConbjvYQ2.js +0 -1
- package/dist/web/_app/immutable/chunks/D0e8PVhd2.js +0 -1
- package/dist/web/_app/immutable/chunks/D8tqcUuZ.js +0 -1
- package/dist/web/_app/immutable/chunks/D9PNm1z6.js +0 -1
- package/dist/web/_app/immutable/chunks/DO51xrVU.js +0 -1
- package/dist/web/_app/immutable/chunks/DT8BrRgZ.js +0 -1
- package/dist/web/_app/immutable/chunks/DU5E-t7g.js +0 -1
- package/dist/web/_app/immutable/chunks/DYdnKpvC2.js +0 -2
- package/dist/web/_app/immutable/chunks/DfGc2Vbl.js +0 -1
- package/dist/web/_app/immutable/chunks/Dvmab3aw2.js +0 -1
- package/dist/web/_app/immutable/chunks/JKJTTpmw.js +0 -1
- package/dist/web/_app/immutable/chunks/LB81VOAq.js +0 -1
- package/dist/web/_app/immutable/chunks/SRjhqDO_.js +0 -1
- package/dist/web/_app/immutable/chunks/YyBqtYe3.js +0 -1
- package/dist/web/_app/immutable/chunks/gULFBDXq2.js +0 -83
- package/dist/web/_app/immutable/entry/app.Bx-aFaCy.js +0 -2
- package/dist/web/_app/immutable/entry/start.B58d5Pp4.js +0 -1
- package/dist/web/_app/immutable/nodes/2.CuIyeEFg.js +0 -106
- package/dist/web/_app/immutable/nodes/4.BOsIV0oS.js +0 -2
- /package/dist/web/_app/immutable/chunks/{ByqcwQIl.js → 0y07qhHv.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{C0Prwfom.js → 3Ddap93k.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{CU3SD7Uz.js → BAQqEuSW.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{CQzfplZr.js → BAwCCELm.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{C50qCxM2.js → BBVt71H0.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{BY2QDF2w2.js → BJDI0CVm2.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{Cd727XFk.js → BLZ_gUXk.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DuxlyVaW.js → BM8OckeF.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{BRcFsc6v.js → BNjJ851o.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DTsh-rY8.js → BQAzQWNc.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{BSz-Rs6A.js → BaRWyZJp.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{XMWvwY0B2.js → BdEEvMm72.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{Dpq3LnpX.js → BzdIirhc.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{BzeVNab0.js → C4ezn1Zj.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{Mj7EytRp.js → C4q5lyFs.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{C4_lca3x.js → C4qg8l0L2.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DQKRvmzW.js → C4wPmO56.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DxAl1xVd.js → CBNzpT1u.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{C9bebE2N.js → CBb1eVhA.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{_3ZPFErk.js → CCvrA-Jl.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DllsEP8T.js → CLHWE-_g.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{C2A9kXuc.js → CQ-uDjOs.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{A6bT_qFs.js → CgE_evqe2.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DazIzF5w.js → ChSBCyG-.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{Ct9eObjX2.js → ChdqjvhH2.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{Br5pOq3s2.js → CjiKtAHC2.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DihOyosf2.js → CltHJTT32.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{UkaYCElu.js → CnXnpq3_.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{c-GqbtBN.js → Cw58x-kY.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DW4yjnj8.js → D1f8I7Vy.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{BgGI_ZrC.js → D6KPkxcJ.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DZZTHHPW.js → DP3v2upT.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{BuHDhb_3.js → Dsmkh2We.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{NKbVWJ8w.js → DzMSiHnE.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{Dwtqnses.js → E1QJ8qxh.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DhF8zQQ3.js → EW0uyMSW.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DPqDmSD4.js → YrKPpvrI2.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{DLG3I4qA.js → hEijmR6g.js} +0 -0
- /package/dist/web/_app/immutable/chunks/{CsmcxOCI2.js → wR_dFW8K2.js} +0 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph admin backup — produce a passphrase-wrapped, manifest-aware
|
|
3
|
+
* snapshot of the entire local workspace.
|
|
4
|
+
*
|
|
5
|
+
* Pairs with `admin restore`. Distinct from `clone`:
|
|
6
|
+
* - `clone` pulls from a running server; strips machine-specific config
|
|
7
|
+
* (r2_config, railway, master-key, JWT secret); good for setting up
|
|
8
|
+
* a new dev machine.
|
|
9
|
+
* - `admin backup` snapshots the local workspace; includes secrets.db,
|
|
10
|
+
* auth.db, r2_config, railway IDs, and a manifest of the Railway env
|
|
11
|
+
* vars (master key, JWT secret) the owner provides at backup time;
|
|
12
|
+
* good for "server is dead, restore me."
|
|
13
|
+
*
|
|
14
|
+
* Bundle contents (encrypted as a single .sgbackup file):
|
|
15
|
+
* - manifest.json — env-var values + Railway IDs + workspace name + ts
|
|
16
|
+
* - workspace/ — tar of the workspace directory, minus
|
|
17
|
+
* node_modules/, .DS_Store, and any *.log files
|
|
18
|
+
*
|
|
19
|
+
* Crypto: passphrase → scrypt → key → xchacha20poly1305(tar.gz).
|
|
20
|
+
* See [src/cli/crypto-bundle.ts] for the wire format.
|
|
21
|
+
*
|
|
22
|
+
* Refuses to write the bundle to a destination directory unless it
|
|
23
|
+
* exists with mode 0700, or is being created (we make it 0700). Bundle
|
|
24
|
+
* file is written with mode 0600. Defense against world-readable backups
|
|
25
|
+
* that contain bcrypt hashes from auth.db.
|
|
26
|
+
*/
|
|
27
|
+
import { Command } from 'commander';
|
|
28
|
+
interface BackupManifest {
|
|
29
|
+
workspace_name: string;
|
|
30
|
+
created_at: string;
|
|
31
|
+
format_version: 1;
|
|
32
|
+
/** Railway-side env vars the operator supplied at backup time. The
|
|
33
|
+
* backup intentionally re-prompts for these (rather than reading from
|
|
34
|
+
* the workspace's secrets.db) so a single passphrase-wrapped bundle
|
|
35
|
+
* carries everything restore needs. */
|
|
36
|
+
env: {
|
|
37
|
+
STUDIOGRAPH_MASTER_KEY: string;
|
|
38
|
+
STUDIOGRAPH_JWT_SECRET: string;
|
|
39
|
+
};
|
|
40
|
+
/** Railway link, if any. Each field is optional in workspace.json
|
|
41
|
+
* schema, so the manifest carries them as optional too — restore
|
|
42
|
+
* reports the gap if a value is missing rather than rejecting the
|
|
43
|
+
* whole bundle. */
|
|
44
|
+
railway?: {
|
|
45
|
+
project_id?: string;
|
|
46
|
+
service_id?: string;
|
|
47
|
+
environment_id?: string;
|
|
48
|
+
};
|
|
49
|
+
/** Cosmetic only — restore uses this to print "this bundle is from
|
|
50
|
+
* workspace X, created Y". Does not affect the restore behavior. */
|
|
51
|
+
notes?: string;
|
|
52
|
+
}
|
|
53
|
+
export declare const adminBackupCommand: Command;
|
|
54
|
+
export type { BackupManifest };
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph admin backup — produce a passphrase-wrapped, manifest-aware
|
|
3
|
+
* snapshot of the entire local workspace.
|
|
4
|
+
*
|
|
5
|
+
* Pairs with `admin restore`. Distinct from `clone`:
|
|
6
|
+
* - `clone` pulls from a running server; strips machine-specific config
|
|
7
|
+
* (r2_config, railway, master-key, JWT secret); good for setting up
|
|
8
|
+
* a new dev machine.
|
|
9
|
+
* - `admin backup` snapshots the local workspace; includes secrets.db,
|
|
10
|
+
* auth.db, r2_config, railway IDs, and a manifest of the Railway env
|
|
11
|
+
* vars (master key, JWT secret) the owner provides at backup time;
|
|
12
|
+
* good for "server is dead, restore me."
|
|
13
|
+
*
|
|
14
|
+
* Bundle contents (encrypted as a single .sgbackup file):
|
|
15
|
+
* - manifest.json — env-var values + Railway IDs + workspace name + ts
|
|
16
|
+
* - workspace/ — tar of the workspace directory, minus
|
|
17
|
+
* node_modules/, .DS_Store, and any *.log files
|
|
18
|
+
*
|
|
19
|
+
* Crypto: passphrase → scrypt → key → xchacha20poly1305(tar.gz).
|
|
20
|
+
* See [src/cli/crypto-bundle.ts] for the wire format.
|
|
21
|
+
*
|
|
22
|
+
* Refuses to write the bundle to a destination directory unless it
|
|
23
|
+
* exists with mode 0700, or is being created (we make it 0700). Bundle
|
|
24
|
+
* file is written with mode 0600. Defense against world-readable backups
|
|
25
|
+
* that contain bcrypt hashes from auth.db.
|
|
26
|
+
*/
|
|
27
|
+
import { Command } from 'commander';
|
|
28
|
+
import { intro, outro, log, spinner, password, isCancel, confirm } from '@clack/prompts';
|
|
29
|
+
import { execFileSync } from 'child_process';
|
|
30
|
+
import { existsSync, mkdirSync, writeFileSync, statSync, mkdtempSync, rmSync, readFileSync } from 'fs';
|
|
31
|
+
import { join, resolve } from 'path';
|
|
32
|
+
import { tmpdir } from 'os';
|
|
33
|
+
import { Workspace } from '../../../core/workspace.js';
|
|
34
|
+
import { sealBundle } from '../../crypto-bundle.js';
|
|
35
|
+
export const adminBackupCommand = new Command('backup')
|
|
36
|
+
.description('Snapshot the local workspace into a passphrase-wrapped bundle')
|
|
37
|
+
.option('--out <path>', 'Output directory for the .sgbackup file (default: cwd)')
|
|
38
|
+
.option('--master-key <value>', 'STUDIOGRAPH_MASTER_KEY env value (also reads STUDIOGRAPH_MASTER_KEY env var)')
|
|
39
|
+
.option('--jwt-secret <value>', 'STUDIOGRAPH_JWT_SECRET env value (also reads STUDIOGRAPH_JWT_SECRET env var)')
|
|
40
|
+
.option('--passphrase-stdin', 'Read the bundle passphrase from stdin (single line)')
|
|
41
|
+
.action(async (opts) => {
|
|
42
|
+
intro('Studiograph workspace backup');
|
|
43
|
+
const workspace = new Workspace();
|
|
44
|
+
if (!workspace.isWorkspace()) {
|
|
45
|
+
outro('Not a Studiograph workspace. Run from your workspace root.');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const config = await workspace.loadConfig();
|
|
50
|
+
// Step 1: gather the env-var manifest. These are NOT in the workspace
|
|
51
|
+
// dir — they live in Railway's variables. The backup is useless
|
|
52
|
+
// without them, so we make collecting them an explicit, blocking
|
|
53
|
+
// step.
|
|
54
|
+
const masterKey = opts.masterKey?.trim() || process.env.STUDIOGRAPH_MASTER_KEY?.trim();
|
|
55
|
+
const jwtSecret = opts.jwtSecret?.trim() || process.env.STUDIOGRAPH_JWT_SECRET?.trim();
|
|
56
|
+
let mk;
|
|
57
|
+
let jwt;
|
|
58
|
+
if (masterKey && jwtSecret) {
|
|
59
|
+
mk = masterKey;
|
|
60
|
+
jwt = jwtSecret;
|
|
61
|
+
log.info('Using STUDIOGRAPH_MASTER_KEY + STUDIOGRAPH_JWT_SECRET from flags/env');
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
if (!process.stdout.isTTY) {
|
|
65
|
+
outro('No TTY. Pass --master-key and --jwt-secret, or set the matching env vars.');
|
|
66
|
+
process.exit(1);
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
log.message('These two values live in Railway, not in the workspace. Copy them from\n' +
|
|
70
|
+
' Railway → your service → Variables → STUDIOGRAPH_MASTER_KEY / STUDIOGRAPH_JWT_SECRET\n' +
|
|
71
|
+
'Without them the restored workspace cannot decrypt secrets.db or verify any\n' +
|
|
72
|
+
'existing JWT cookies.');
|
|
73
|
+
const mkInput = await password({ message: 'STUDIOGRAPH_MASTER_KEY:', validate: (v) => v.trim() ? undefined : 'Required' });
|
|
74
|
+
if (isCancel(mkInput)) {
|
|
75
|
+
outro('Cancelled');
|
|
76
|
+
process.exit(0);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
const jwtInput = await password({ message: 'STUDIOGRAPH_JWT_SECRET:', validate: (v) => v.trim() ? undefined : 'Required' });
|
|
80
|
+
if (isCancel(jwtInput)) {
|
|
81
|
+
outro('Cancelled');
|
|
82
|
+
process.exit(0);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
mk = String(mkInput).trim();
|
|
86
|
+
jwt = String(jwtInput).trim();
|
|
87
|
+
}
|
|
88
|
+
// Step 2: passphrase for the bundle.
|
|
89
|
+
let passphrase;
|
|
90
|
+
if (opts.passphraseStdin) {
|
|
91
|
+
passphrase = await readPassphraseStdin();
|
|
92
|
+
}
|
|
93
|
+
else if (process.stdout.isTTY) {
|
|
94
|
+
const pp1 = await password({
|
|
95
|
+
message: 'Bundle passphrase (min 12 chars):',
|
|
96
|
+
validate: (v) => (v.length >= 12 ? undefined : 'At least 12 characters'),
|
|
97
|
+
});
|
|
98
|
+
if (isCancel(pp1)) {
|
|
99
|
+
outro('Cancelled');
|
|
100
|
+
process.exit(0);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
const pp2 = await password({ message: 'Confirm passphrase:' });
|
|
104
|
+
if (isCancel(pp2)) {
|
|
105
|
+
outro('Cancelled');
|
|
106
|
+
process.exit(0);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (String(pp1) !== String(pp2)) {
|
|
110
|
+
outro('Passphrases did not match.');
|
|
111
|
+
process.exit(1);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
passphrase = String(pp1);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
outro('No TTY. Pass --passphrase-stdin and pipe the passphrase to stdin.');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Step 3: validate output directory + file.
|
|
122
|
+
const outDir = resolve(opts.out ?? process.cwd());
|
|
123
|
+
if (!existsSync(outDir)) {
|
|
124
|
+
mkdirSync(outDir, { recursive: true, mode: 0o700 });
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const dirMode = statSync(outDir).mode & 0o777;
|
|
128
|
+
if ((dirMode & 0o077) !== 0) {
|
|
129
|
+
const proceed = await confirm({
|
|
130
|
+
message: `Output directory ${outDir} is mode 0${dirMode.toString(8)} — group/world readable. Continue anyway?`,
|
|
131
|
+
initialValue: false,
|
|
132
|
+
});
|
|
133
|
+
if (!proceed || isCancel(proceed)) {
|
|
134
|
+
outro('Cancelled — chmod 700 the directory and retry.');
|
|
135
|
+
process.exit(0);
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const fileName = `studiograph-backup-${(config.team_name || 'workspace').toLowerCase().replace(/[^a-z0-9]+/g, '-')}-${new Date().toISOString().slice(0, 10)}.sgbackup`;
|
|
141
|
+
const filePath = join(outDir, fileName);
|
|
142
|
+
if (existsSync(filePath)) {
|
|
143
|
+
outro(`Refusing to overwrite ${filePath}. Move or delete the existing bundle and retry.`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
// Step 4: build the bundle. tar the workspace + manifest into a temp
|
|
148
|
+
// file, encrypt, write. Temp lives in os.tmpdir(); we clean up in
|
|
149
|
+
// finally even on errors.
|
|
150
|
+
const tmpRoot = mkdtempSync(join(tmpdir(), 'sg-backup-'));
|
|
151
|
+
const manifestPath = join(tmpRoot, 'manifest.json');
|
|
152
|
+
const tarPath = join(tmpRoot, 'bundle.tar.gz');
|
|
153
|
+
try {
|
|
154
|
+
const manifest = {
|
|
155
|
+
workspace_name: config.team_name || 'unknown',
|
|
156
|
+
created_at: new Date().toISOString(),
|
|
157
|
+
format_version: 1,
|
|
158
|
+
env: {
|
|
159
|
+
STUDIOGRAPH_MASTER_KEY: mk,
|
|
160
|
+
STUDIOGRAPH_JWT_SECRET: jwt,
|
|
161
|
+
},
|
|
162
|
+
railway: config.railway,
|
|
163
|
+
};
|
|
164
|
+
writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), { encoding: 'utf-8', mode: 0o600 });
|
|
165
|
+
const spin = spinner();
|
|
166
|
+
spin.start('Tarring workspace...');
|
|
167
|
+
// -C tmpRoot for manifest.json + -C cwd for workspace contents.
|
|
168
|
+
// Two `-C` flags = tar resets the chdir per following path. The
|
|
169
|
+
// workspace contents land at top-level of the tar so restore is
|
|
170
|
+
// a single expand. The manifest sits beside them.
|
|
171
|
+
// Exclusions: node_modules, .DS_Store, *.log keep the bundle lean
|
|
172
|
+
// without dropping anything load-bearing.
|
|
173
|
+
execFileSync('tar', [
|
|
174
|
+
'-czf', tarPath,
|
|
175
|
+
'--exclude', 'node_modules',
|
|
176
|
+
'--exclude', '.DS_Store',
|
|
177
|
+
'--exclude', '*.log',
|
|
178
|
+
'-C', tmpRoot, 'manifest.json',
|
|
179
|
+
'-C', process.cwd(), '.',
|
|
180
|
+
], { stdio: 'pipe' });
|
|
181
|
+
spin.stop('Tarred');
|
|
182
|
+
spin.start('Encrypting (scrypt is intentionally slow)...');
|
|
183
|
+
const plaintext = readFileSync(tarPath);
|
|
184
|
+
const ciphertext = sealBundle(plaintext, passphrase);
|
|
185
|
+
spin.stop('Encrypted');
|
|
186
|
+
writeFileSync(filePath, ciphertext, { mode: 0o600 });
|
|
187
|
+
log.success(`Wrote ${filePath} (${(ciphertext.length / 1024).toFixed(1)} KB)`);
|
|
188
|
+
log.message('Keep this file AND its passphrase separately. The bundle contains\n' +
|
|
189
|
+
'bcrypt password hashes, encrypted secrets.db, and your Railway env\n' +
|
|
190
|
+
'vars in plaintext (within the encrypted envelope) — treat it like\n' +
|
|
191
|
+
'a credential.');
|
|
192
|
+
outro('Backup complete.');
|
|
193
|
+
}
|
|
194
|
+
finally {
|
|
195
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
async function readPassphraseStdin() {
|
|
199
|
+
return new Promise((resolveFn, rejectFn) => {
|
|
200
|
+
let buf = '';
|
|
201
|
+
process.stdin.setEncoding('utf-8');
|
|
202
|
+
process.stdin.on('data', (chunk) => (buf += chunk));
|
|
203
|
+
process.stdin.on('end', () => resolveFn(buf.replace(/\r?\n$/, '')));
|
|
204
|
+
process.stdin.on('error', rejectFn);
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=backup.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backup.js","sourceRoot":"","sources":["../../../../src/cli/commands/admin/backup.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph folder command
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* studiograph folder add <name> — create a new folder
|
|
6
|
+
* studiograph folder list — list all folders with entity counts
|
|
7
|
+
* studiograph folder rename <old> <new> — rename a folder
|
|
8
|
+
* studiograph folder remove <name> — remove a folder (with confirmation)
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
export declare const folderCommand: Command;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph folder command
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* studiograph folder add <name> — create a new folder
|
|
6
|
+
* studiograph folder list — list all folders with entity counts
|
|
7
|
+
* studiograph folder rename <old> <new> — rename a folder
|
|
8
|
+
* studiograph folder remove <name> — remove a folder (with confirmation)
|
|
9
|
+
*/
|
|
10
|
+
import { Command } from 'commander';
|
|
11
|
+
import { log, confirm } from '@clack/prompts';
|
|
12
|
+
import { existsSync } from 'fs';
|
|
13
|
+
import { join } from 'path';
|
|
14
|
+
import { Workspace } from '../../../core/workspace.js';
|
|
15
|
+
import { BaseGraphManager } from '../../../core/graph.js';
|
|
16
|
+
function requireWorkspace() {
|
|
17
|
+
const workspace = new Workspace();
|
|
18
|
+
if (!workspace.isWorkspace()) {
|
|
19
|
+
console.error('Not a Studiograph workspace. Run `studiograph init` first.');
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return workspace;
|
|
23
|
+
}
|
|
24
|
+
// ─── add ──────────────────────────────────────────────────────────────────────
|
|
25
|
+
const addCommand = new Command('add')
|
|
26
|
+
.description('Create a new folder')
|
|
27
|
+
.argument('<name>', 'Folder name (kebab-case)')
|
|
28
|
+
.option('-d, --description <desc>', 'Folder description')
|
|
29
|
+
.action(async (name, opts) => {
|
|
30
|
+
const workspace = requireWorkspace();
|
|
31
|
+
if (!workspace) {
|
|
32
|
+
process.exit(1);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
// Validate name is kebab-case
|
|
36
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(name)) {
|
|
37
|
+
log.error('Folder name must be kebab-case (e.g., "client-projects")');
|
|
38
|
+
process.exit(1);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
try {
|
|
42
|
+
const repo = await workspace.registerRepo({
|
|
43
|
+
name,
|
|
44
|
+
description: opts.description,
|
|
45
|
+
});
|
|
46
|
+
log.success(`Folder "${repo.name}" created at ${repo.path}/`);
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
log.error(err instanceof Error ? err.message : 'Unknown error');
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
// ─── list ─────────────────────────────────────────────────────────────────────
|
|
54
|
+
const listCommand = new Command('list')
|
|
55
|
+
.description('List all folders with entity counts')
|
|
56
|
+
.action(async () => {
|
|
57
|
+
const workspace = requireWorkspace();
|
|
58
|
+
if (!workspace) {
|
|
59
|
+
process.exit(1);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const config = await workspace.loadConfig();
|
|
63
|
+
const repos = config.repos;
|
|
64
|
+
if (repos.length === 0) {
|
|
65
|
+
log.info('No folders. Run `studiograph folder add <name>` to create one.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
console.log('\nFolders\n');
|
|
69
|
+
console.log(' Name Entities Description');
|
|
70
|
+
console.log(' ' + '\u2500'.repeat(72));
|
|
71
|
+
for (const repo of repos) {
|
|
72
|
+
let entityCount = 0;
|
|
73
|
+
const repoPath = join(workspace.getWorkspacePath(), repo.path);
|
|
74
|
+
if (existsSync(repoPath)) {
|
|
75
|
+
try {
|
|
76
|
+
const graph = new BaseGraphManager({
|
|
77
|
+
repoPath,
|
|
78
|
+
repoName: repo.name,
|
|
79
|
+
gitUser: { id: 'cli', name: 'cli', email: 'cli@studiograph.local' },
|
|
80
|
+
});
|
|
81
|
+
entityCount = graph.list().length;
|
|
82
|
+
}
|
|
83
|
+
catch { /* non-fatal */ }
|
|
84
|
+
}
|
|
85
|
+
const rname = repo.name.padEnd(30);
|
|
86
|
+
const count = String(entityCount).padStart(4);
|
|
87
|
+
const desc = repo.description || '';
|
|
88
|
+
const suffix = repo.personal ? ' (personal)' : '';
|
|
89
|
+
console.log(` ${rname} ${count} ${desc}${suffix}`);
|
|
90
|
+
}
|
|
91
|
+
console.log('');
|
|
92
|
+
});
|
|
93
|
+
// ─── rename ───────────────────────────────────────────────────────────────────
|
|
94
|
+
const renameCommand = new Command('rename')
|
|
95
|
+
.description('Rename a folder')
|
|
96
|
+
.argument('<old-name>', 'Current folder name')
|
|
97
|
+
.argument('<new-name>', 'New folder name (kebab-case)')
|
|
98
|
+
.action(async (oldName, newName) => {
|
|
99
|
+
const workspace = requireWorkspace();
|
|
100
|
+
if (!workspace) {
|
|
101
|
+
process.exit(1);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
if (!/^[a-z0-9][a-z0-9-]*$/.test(newName)) {
|
|
105
|
+
log.error('New name must be kebab-case (e.g., "client-projects")');
|
|
106
|
+
process.exit(1);
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const config = await workspace.loadConfig();
|
|
110
|
+
const repo = config.repos.find(r => r.name === oldName);
|
|
111
|
+
if (!repo) {
|
|
112
|
+
log.error(`Folder "${oldName}" not found.`);
|
|
113
|
+
process.exit(1);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (config.repos.some(r => r.name === newName)) {
|
|
117
|
+
log.error(`Folder "${newName}" already exists.`);
|
|
118
|
+
process.exit(1);
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const oldPath = join(workspace.getWorkspacePath(), repo.path);
|
|
122
|
+
const newPath = join(workspace.getWorkspacePath(), newName);
|
|
123
|
+
// Rename directory
|
|
124
|
+
if (existsSync(oldPath)) {
|
|
125
|
+
const { renameSync } = await import('fs');
|
|
126
|
+
renameSync(oldPath, newPath);
|
|
127
|
+
}
|
|
128
|
+
// Update config
|
|
129
|
+
repo.name = newName;
|
|
130
|
+
repo.path = newName;
|
|
131
|
+
workspace.writeConfig(config);
|
|
132
|
+
// Update .studiograph marker in the repo
|
|
133
|
+
const markerPath = join(newPath, '.studiograph');
|
|
134
|
+
if (existsSync(markerPath)) {
|
|
135
|
+
const { writeFileSync } = await import('fs');
|
|
136
|
+
writeFileSync(markerPath, JSON.stringify({ repo_name: newName }, null, 2), 'utf-8');
|
|
137
|
+
}
|
|
138
|
+
log.success(`Folder renamed: "${oldName}" → "${newName}"`);
|
|
139
|
+
});
|
|
140
|
+
// ─── remove ───────────────────────────────────────────────────────────────────
|
|
141
|
+
const removeCommand = new Command('remove')
|
|
142
|
+
.description('Remove a folder from the workspace')
|
|
143
|
+
.argument('<name>', 'Folder name to remove')
|
|
144
|
+
.option('--delete-files', 'Also delete the folder directory from disk')
|
|
145
|
+
.action(async (name, opts) => {
|
|
146
|
+
const workspace = requireWorkspace();
|
|
147
|
+
if (!workspace) {
|
|
148
|
+
process.exit(1);
|
|
149
|
+
return;
|
|
150
|
+
}
|
|
151
|
+
const config = await workspace.loadConfig();
|
|
152
|
+
const repoIndex = config.repos.findIndex(r => r.name === name);
|
|
153
|
+
if (repoIndex === -1) {
|
|
154
|
+
log.error(`Folder "${name}" not found.`);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
const repo = config.repos[repoIndex];
|
|
159
|
+
const repoPath = join(workspace.getWorkspacePath(), repo.path);
|
|
160
|
+
const yes = await confirm({
|
|
161
|
+
message: opts.deleteFiles
|
|
162
|
+
? `Remove folder "${name}" and delete all files at ${repo.path}/?`
|
|
163
|
+
: `Remove folder "${name}" from workspace config? (files at ${repo.path}/ will be kept)`,
|
|
164
|
+
});
|
|
165
|
+
if (!yes || typeof yes === 'symbol')
|
|
166
|
+
return;
|
|
167
|
+
// Remove from config
|
|
168
|
+
config.repos.splice(repoIndex, 1);
|
|
169
|
+
workspace.writeConfig(config);
|
|
170
|
+
if (opts.deleteFiles && existsSync(repoPath)) {
|
|
171
|
+
const { rmSync } = await import('fs');
|
|
172
|
+
rmSync(repoPath, { recursive: true, force: true });
|
|
173
|
+
log.success(`Folder "${name}" removed and files deleted.`);
|
|
174
|
+
}
|
|
175
|
+
else {
|
|
176
|
+
log.success(`Folder "${name}" removed from config. Files at ${repo.path}/ were kept.`);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
// ─── parent command ───────────────────────────────────────────────────────────
|
|
180
|
+
export const folderCommand = new Command('folder')
|
|
181
|
+
.description('Manage workspace folders')
|
|
182
|
+
.addCommand(addCommand)
|
|
183
|
+
.addCommand(listCommand)
|
|
184
|
+
.addCommand(renameCommand)
|
|
185
|
+
.addCommand(removeCommand);
|
|
186
|
+
//# sourceMappingURL=folder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder.js","sourceRoot":"","sources":["../../../../src/cli/commands/admin/folder.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph admin — owner-only ops surface.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands accumulate here as Phase A lands:
|
|
5
|
+
* - admin token (A4)
|
|
6
|
+
* - admin migrate (A5)
|
|
7
|
+
* - admin backup (A6)
|
|
8
|
+
* - admin restore (A6)
|
|
9
|
+
* - admin transfer-ownership (A8)
|
|
10
|
+
* - admin audit-workspace (A8)
|
|
11
|
+
*
|
|
12
|
+
* In Phase D, the existing top-level user/folder/secrets/settings/audit
|
|
13
|
+
* commands also move under this parent.
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
export declare const adminCommand: Command;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph admin — owner-only ops surface.
|
|
3
|
+
*
|
|
4
|
+
* Subcommands accumulate here as Phase A lands:
|
|
5
|
+
* - admin token (A4)
|
|
6
|
+
* - admin migrate (A5)
|
|
7
|
+
* - admin backup (A6)
|
|
8
|
+
* - admin restore (A6)
|
|
9
|
+
* - admin transfer-ownership (A8)
|
|
10
|
+
* - admin audit-workspace (A8)
|
|
11
|
+
*
|
|
12
|
+
* In Phase D, the existing top-level user/folder/secrets/settings/audit
|
|
13
|
+
* commands also move under this parent.
|
|
14
|
+
*/
|
|
15
|
+
import { Command } from 'commander';
|
|
16
|
+
import { adminTokenCommand } from './token.js';
|
|
17
|
+
import { adminMigrateCommand } from './migrate.js';
|
|
18
|
+
import { adminBackupCommand } from './backup.js';
|
|
19
|
+
import { adminRestoreCommand } from './restore.js';
|
|
20
|
+
import { adminTransferOwnershipCommand } from './transfer-ownership.js';
|
|
21
|
+
import { adminAuditWorkspaceCommand } from './audit-workspace.js';
|
|
22
|
+
import { userCommand } from './user.js';
|
|
23
|
+
import { folderCommand } from './folder.js';
|
|
24
|
+
import { secretsCommand } from './secrets.js';
|
|
25
|
+
import { settingsCommand } from './settings.js';
|
|
26
|
+
import { auditCommand } from './audit.js';
|
|
27
|
+
export const adminCommand = new Command('admin').description('Owner-only administrative commands');
|
|
28
|
+
// Phase D1: the five workspace-management subcommands (user, folder,
|
|
29
|
+
// secrets, settings, audit) moved from top-level into admin/. Top-level
|
|
30
|
+
// aliases stay for one release — see src/cli/index.ts argv shim. The
|
|
31
|
+
// next release drops the shim and these become admin-only.
|
|
32
|
+
adminCommand.addCommand(userCommand);
|
|
33
|
+
adminCommand.addCommand(folderCommand);
|
|
34
|
+
adminCommand.addCommand(secretsCommand);
|
|
35
|
+
adminCommand.addCommand(settingsCommand);
|
|
36
|
+
adminCommand.addCommand(auditCommand);
|
|
37
|
+
// New admin verbs added through Phase A.
|
|
38
|
+
adminCommand.addCommand(adminTokenCommand);
|
|
39
|
+
adminCommand.addCommand(adminMigrateCommand);
|
|
40
|
+
adminCommand.addCommand(adminBackupCommand);
|
|
41
|
+
adminCommand.addCommand(adminRestoreCommand);
|
|
42
|
+
adminCommand.addCommand(adminTransferOwnershipCommand);
|
|
43
|
+
adminCommand.addCommand(adminAuditWorkspaceCommand);
|
|
44
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/cli/commands/admin/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,OAAO,EAAE,6BAA6B,EAAE,MAAM,yBAAyB,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,sBAAsB,CAAC;AAClE,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,MAAM,CAAC,MAAM,YAAY,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC,WAAW,CAAC,oCAAoC,CAAC,CAAC;AAEnG,qEAAqE;AACrE,wEAAwE;AACxE,qEAAqE;AACrE,2DAA2D;AAC3D,YAAY,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;AACrC,YAAY,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AACvC,YAAY,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;AACxC,YAAY,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AACzC,YAAY,CAAC,UAAU,CAAC,YAAY,CAAC,CAAC;AAEtC,yCAAyC;AACzC,YAAY,CAAC,UAAU,CAAC,iBAAiB,CAAC,CAAC;AAC3C,YAAY,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAC7C,YAAY,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;AAC5C,YAAY,CAAC,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAC7C,YAAY,CAAC,UAAU,CAAC,6BAA6B,CAAC,CAAC;AACvD,YAAY,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studiograph admin migrate — bulk regex find-replace across workspace repos.
|
|
3
|
+
*
|
|
4
|
+
* Codifies the hand-rolled sed pattern the owner has reached for twice
|
|
5
|
+
* during schema renames. Walks every repo in workspace.json (or a
|
|
6
|
+
* `--repos`-filtered subset), applies the regex, and produces one commit
|
|
7
|
+
* per repo with the same message. `--dry-run` reports without writing.
|
|
8
|
+
* `--also-rename-files` does a second pass that `git mv`s any files
|
|
9
|
+
* whose name matches the regex.
|
|
10
|
+
*
|
|
11
|
+
* Safety:
|
|
12
|
+
* - Refuses if ANY targeted repo has uncommitted changes. Catches
|
|
13
|
+
* half-finished local edits before they're caught up in the rewrite.
|
|
14
|
+
* - Runs the regex against the file contents read with utf-8; commit
|
|
15
|
+
* uses `git -c core.quotepath=false` so non-ASCII file paths in
|
|
16
|
+
* summaries don't render as `\xxx` escapes.
|
|
17
|
+
* - Skips the .git/, .studiograph/, and node_modules/ subtrees of every
|
|
18
|
+
* repo (defensive — none of those should ever match a workspace glob,
|
|
19
|
+
* but a stray `--in '**'` shouldn't pull them in either).
|
|
20
|
+
*/
|
|
21
|
+
import { Command } from 'commander';
|
|
22
|
+
import type { RepoConfig } from '../../../core/types.js';
|
|
23
|
+
interface RepoChange {
|
|
24
|
+
repoName: string;
|
|
25
|
+
repoPath: string;
|
|
26
|
+
fileChanges: Array<{
|
|
27
|
+
relPath: string;
|
|
28
|
+
replacements: number;
|
|
29
|
+
}>;
|
|
30
|
+
fileRenames: Array<{
|
|
31
|
+
from: string;
|
|
32
|
+
to: string;
|
|
33
|
+
}>;
|
|
34
|
+
}
|
|
35
|
+
export declare const adminMigrateCommand: Command;
|
|
36
|
+
/**
|
|
37
|
+
* Walk one repo, regex-replace contents in every file matching `pattern`
|
|
38
|
+
* (the glob pattern, not the regex). Returns a summary; writes files
|
|
39
|
+
* unless `dryRun` is true.
|
|
40
|
+
*/
|
|
41
|
+
export declare function migrateRepoContent(repo: RepoConfig, globPattern: string, regex: RegExp, replacement: string, dryRun: boolean): Promise<RepoChange>;
|
|
42
|
+
/**
|
|
43
|
+
* Apply a replacement-string template the way String.prototype.replace
|
|
44
|
+
* does when given a string (not a function): expand $1..$9 to capture
|
|
45
|
+
* groups, $$ to a literal $, $& to the whole match.
|
|
46
|
+
*
|
|
47
|
+
* We re-implement this because we call replace with a *function* (to
|
|
48
|
+
* count matches) but want the literal-string substitution semantics for
|
|
49
|
+
* the actual replacement text. Without this, $1/$2 would render
|
|
50
|
+
* literally instead of being expanded.
|
|
51
|
+
*/
|
|
52
|
+
declare function applyReplacementString(template: string, match: string, args: unknown[]): string;
|
|
53
|
+
export declare const __internals: {
|
|
54
|
+
applyReplacementString: typeof applyReplacementString;
|
|
55
|
+
};
|
|
56
|
+
export {};
|