thinkncollab-shell 0.0.19 → 2.0.0-beta.1

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.
Files changed (201) hide show
  1. package/LICENSE +82 -0
  2. package/Readme.md +246 -502
  3. package/dist/bin/thinknsh.js +8 -0
  4. package/dist/src/commands/auth/login.js +7 -0
  5. package/dist/src/commands/auth/logout.js +7 -0
  6. package/dist/src/commands/auth/register.js +7 -0
  7. package/dist/src/commands/auth/whoami.js +7 -0
  8. package/dist/src/commands/dock/bench.js +7 -0
  9. package/dist/src/commands/dock/ci.js +7 -0
  10. package/dist/src/commands/dock/cluster.js +7 -0
  11. package/dist/src/commands/dock/containers.js +7 -0
  12. package/dist/src/commands/dock/dock.js +7 -0
  13. package/dist/src/commands/github/commits.js +7 -0
  14. package/dist/src/commands/github/issue-create.js +7 -0
  15. package/dist/src/commands/github/issues.js +7 -0
  16. package/dist/src/commands/github/pulls.js +7 -0
  17. package/dist/src/commands/github/statusgh.js +7 -0
  18. package/dist/src/commands/index.js +7 -0
  19. package/dist/src/commands/message/say.js +7 -0
  20. package/dist/src/commands/room/join.js +7 -0
  21. package/dist/src/commands/room/leave.js +7 -0
  22. package/dist/src/commands/room/rooms.js +7 -0
  23. package/dist/src/commands/room/sync.js +7 -0
  24. package/dist/src/commands/sandbox/console.js +7 -0
  25. package/dist/src/commands/sandbox/dep.js +7 -0
  26. package/dist/src/commands/sandbox/deploy.js +7 -0
  27. package/dist/src/commands/sandbox/dev.js +7 -0
  28. package/dist/src/commands/sandbox/exec.js +7 -0
  29. package/dist/src/commands/sandbox/freeze.js +7 -0
  30. package/dist/src/commands/sandbox/push.js +7 -0
  31. package/dist/src/commands/sandbox/resume.js +7 -0
  32. package/dist/src/commands/sandbox/sandbox.js +7 -0
  33. package/dist/src/commands/sandbox/share.js +7 -0
  34. package/dist/src/commands/sandbox/snapshot.js +7 -0
  35. package/dist/src/commands/sandbox/sync-deps.js +7 -0
  36. package/dist/src/commands/shell/doctor.js +7 -0
  37. package/dist/src/commands/shell/notifications.js +7 -0
  38. package/dist/src/commands/shell/notify.js +7 -0
  39. package/dist/src/commands/shell/status.js +7 -0
  40. package/dist/src/commands/shell/version.js +7 -0
  41. package/dist/src/commands/tasks/build.js +7 -0
  42. package/dist/src/commands/tasks/complete.js +7 -0
  43. package/dist/src/commands/tasks/create-task.js +7 -0
  44. package/dist/src/commands/tasks/loadtest.js +7 -0
  45. package/dist/src/commands/tasks/task-status.js +7 -0
  46. package/dist/src/commands/tasks/task.js +7 -0
  47. package/dist/src/commands/tasks/tasks.js +7 -0
  48. package/dist/src/commands/team/invite.js +7 -0
  49. package/dist/src/commands/team/myteam.js +7 -0
  50. package/dist/src/commands/team/teammates.js +7 -0
  51. package/dist/src/commands/terminal/share.js +7 -0
  52. package/dist/src/core/api-client.js +7 -0
  53. package/dist/src/core/crypto/e2ee-vault.js +7 -0
  54. package/dist/src/core/dep-store.js +7 -0
  55. package/dist/src/core/dependency-tracker.js +7 -0
  56. package/dist/src/core/geekload.js +7 -0
  57. package/dist/src/core/local-runner.js +7 -0
  58. package/dist/src/core/multi-lang-scanner.js +7 -0
  59. package/dist/src/core/notification-window.js +8 -0
  60. package/dist/src/core/notify-auth.js +7 -0
  61. package/dist/src/core/project-config.js +7 -0
  62. package/dist/src/core/sandbox/arch-shim.js +7 -0
  63. package/dist/src/core/sandbox/capsule-builder.js +7 -0
  64. package/dist/src/core/sandbox/cgroups.js +7 -0
  65. package/dist/src/core/sandbox/container-monitor.js +7 -0
  66. package/dist/src/core/sandbox/crun-manager.js +7 -0
  67. package/dist/src/core/sandbox/engine.js +7 -0
  68. package/dist/src/core/sandbox/image-manager.js +7 -0
  69. package/dist/src/core/sandbox/image-store.js +7 -0
  70. package/dist/src/core/sandbox/index.js +7 -0
  71. package/dist/src/core/sandbox/macos-sandbox.js +7 -0
  72. package/dist/src/core/sandbox/microvm-manager.js +7 -0
  73. package/dist/src/core/sandbox/native-service-manager.js +7 -0
  74. package/dist/src/core/sandbox/network.js +7 -0
  75. package/dist/src/core/sandbox/oci-exporter.js +7 -0
  76. package/dist/src/core/sandbox/oci-puller.js +7 -0
  77. package/dist/src/core/sandbox/oci-runtime-spec.js +7 -0
  78. package/dist/src/core/sandbox/policy-engine.js +7 -0
  79. package/dist/src/core/sandbox/port-proxy-daemon.js +7 -0
  80. package/dist/src/core/sandbox/room-sync-manager.js +7 -0
  81. package/dist/src/core/sandbox/runtime-manager.js +7 -0
  82. package/dist/src/core/sandbox/snapshot-manager.js +7 -0
  83. package/dist/src/core/sandbox/storage.js +7 -0
  84. package/dist/src/core/sandbox/supervisor.js +7 -0
  85. package/dist/src/core/sandbox/tunnel-daemon.js +7 -0
  86. package/dist/src/core/sandbox/tunnel.js +7 -0
  87. package/dist/src/core/sandbox/window-launcher.js +7 -0
  88. package/dist/src/core/sandbox/windows-wsl2.js +7 -0
  89. package/dist/src/core/sandbox-window.js +8 -0
  90. package/dist/src/core/shell.js +7 -0
  91. package/dist/src/core/websocket.js +7 -0
  92. package/dist/src/dock/api/routes.js +7 -0
  93. package/dist/src/dock/api/server.js +7 -0
  94. package/dist/src/dock/api/wire.js +7 -0
  95. package/dist/src/dock/bench/memory.js +7 -0
  96. package/dist/src/dock/bench/runner.js +7 -0
  97. package/dist/src/dock/build/builder.js +7 -0
  98. package/dist/src/dock/build/buildfile.js +7 -0
  99. package/dist/src/dock/build/buildkit.js +7 -0
  100. package/dist/src/dock/build/changes.js +7 -0
  101. package/dist/src/dock/ci/actions.js +7 -0
  102. package/dist/src/dock/ci/runner.js +7 -0
  103. package/dist/src/dock/ci/workflow.js +7 -0
  104. package/dist/src/dock/cli/api.js +7 -0
  105. package/dist/src/dock/cli/args.js +7 -0
  106. package/dist/src/dock/cli/commands.js +7 -0
  107. package/dist/src/dock/cli/compose.js +7 -0
  108. package/dist/src/dock/cli/context.js +7 -0
  109. package/dist/src/dock/cli/files.js +7 -0
  110. package/dist/src/dock/cli/format.js +7 -0
  111. package/dist/src/dock/cli/manage.js +7 -0
  112. package/dist/src/dock/cli/moved.js +7 -0
  113. package/dist/src/dock/cli/services.js +7 -0
  114. package/dist/src/dock/cli/session.js +7 -0
  115. package/dist/src/dock/cli/workspace.js +7 -0
  116. package/dist/src/dock/client.js +7 -0
  117. package/dist/src/dock/compat/cases.js +7 -0
  118. package/dist/src/dock/compat/runner.js +7 -0
  119. package/dist/src/dock/config.js +7 -0
  120. package/dist/src/dock/daemon-main.js +7 -0
  121. package/dist/src/dock/daemon.js +7 -0
  122. package/dist/src/dock/deploy/compose.js +7 -0
  123. package/dist/src/dock/engine.js +7 -0
  124. package/dist/src/dock/image/archive.js +7 -0
  125. package/dist/src/dock/image/auth.js +7 -0
  126. package/dist/src/dock/image/reference.js +7 -0
  127. package/dist/src/dock/image/registry.js +7 -0
  128. package/dist/src/dock/image/tar.js +7 -0
  129. package/dist/src/dock/index.js +7 -0
  130. package/dist/src/dock/linux/host.js +7 -0
  131. package/dist/src/dock/paths.js +7 -0
  132. package/dist/src/dock/runtime/emulation.js +7 -0
  133. package/dist/src/dock/runtime/fsevents.js +7 -0
  134. package/dist/src/dock/runtime/fsops.js +7 -0
  135. package/dist/src/dock/runtime/hostfwd.js +7 -0
  136. package/dist/src/dock/runtime/logs.js +7 -0
  137. package/dist/src/dock/runtime/network.js +7 -0
  138. package/dist/src/dock/runtime/ports.js +7 -0
  139. package/dist/src/dock/runtime/rootfs.js +7 -0
  140. package/dist/src/dock/runtime/seccomp-profile.LICENSE +202 -0
  141. package/dist/src/dock/runtime/seccomp-profile.json +1227 -0
  142. package/dist/src/dock/runtime/seccomp.js +7 -0
  143. package/dist/src/dock/runtime/spec.js +7 -0
  144. package/dist/src/dock/runtime/stats.js +7 -0
  145. package/dist/src/dock/runtime/volumes.js +7 -0
  146. package/dist/src/dock/selftest.js +7 -0
  147. package/dist/src/dock/setup/app.js +7 -0
  148. package/dist/src/dock/setup/autostart.js +7 -0
  149. package/dist/src/dock/setup/index.js +7 -0
  150. package/dist/src/dock/setup/integrate.js +7 -0
  151. package/dist/src/dock/setup/migrate.js +7 -0
  152. package/dist/src/dock/state/store.js +7 -0
  153. package/dist/src/dock/ui/page.js +385 -0
  154. package/dist/src/dock/ui/server.js +7 -0
  155. package/dist/src/dock/version.js +7 -0
  156. package/dist/src/dock/workspace/apps.js +7 -0
  157. package/dist/src/dock/workspace/workspace.js +7 -0
  158. package/dist/src/events/notifications.js +7 -0
  159. package/dist/src/utils/logger.js +7 -0
  160. package/dist/src/utils/terminalCrypto.js +7 -0
  161. package/package.json +41 -16
  162. package/bin/thinknsh.js +0 -71
  163. package/src/commands/auth/login.js +0 -84
  164. package/src/commands/auth/logout.js +0 -32
  165. package/src/commands/auth/register.js +0 -52
  166. package/src/commands/auth/whoami.js +0 -52
  167. package/src/commands/github/commits.js +0 -58
  168. package/src/commands/github/issue-create.js +0 -59
  169. package/src/commands/github/issues.js +0 -57
  170. package/src/commands/github/pulls.js +0 -58
  171. package/src/commands/github/statusgh.js +0 -50
  172. package/src/commands/index.js +0 -136
  173. package/src/commands/message/say.js +0 -49
  174. package/src/commands/room/join.js +0 -142
  175. package/src/commands/room/leave.js +0 -24
  176. package/src/commands/room/rooms.js +0 -66
  177. package/src/commands/shell/notify.js +0 -88
  178. package/src/commands/shell/status.js +0 -73
  179. package/src/commands/shell/version.js +0 -18
  180. package/src/commands/tasks/build.js +0 -190
  181. package/src/commands/tasks/complete.js +0 -940
  182. package/src/commands/tasks/create-task.js +0 -101
  183. package/src/commands/tasks/loadtest.js +0 -101
  184. package/src/commands/tasks/suggestTask.js +0 -0
  185. package/src/commands/tasks/task-status.js +0 -72
  186. package/src/commands/tasks/task.js +0 -182
  187. package/src/commands/tasks/tasks.js +0 -184
  188. package/src/commands/team/invite.js +0 -46
  189. package/src/commands/team/myteam.js +0 -78
  190. package/src/commands/team/teammates.js +0 -52
  191. package/src/commands/terminal/share.js +0 -261
  192. package/src/core/api-client.js +0 -246
  193. package/src/core/geekload.js +0 -524
  194. package/src/core/local-runner.js +0 -861
  195. package/src/core/notification-window.js +0 -165
  196. package/src/core/project-config.js +0 -100
  197. package/src/core/shell.js +0 -734
  198. package/src/core/websocket.js +0 -482
  199. package/src/events/notifications.js +0 -184
  200. package/src/utils/logger.js +0 -159
  201. package/src/utils/terminalCrypto.js +0 -47
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const fs=require("fs-extra"),path=require("path"),crypto=require("crypto"),chalk=require("chalk"),{execSync}=require("child_process"),ImageManager=require("./image-manager");class CapsuleBuilder{constructor(e=process.cwd()){this.contextDir=path.resolve(e),this.imageManager=new ImageManager,this.layersDir=path.join(this.imageManager.baseDir,"layers"),fs.ensureDirSync(this.layersDir),this.dockerignore=this._loadDockerignore()}_loadDockerignore(){const e=path.join(this.contextDir,".dockerignore");if(!fs.existsSync(e))return[];try{return fs.readFileSync(e,"utf8").split("\n").map(e=>e.trim()).filter(e=>e&&!e.startsWith("#"))}catch(e){return[]}}_isIgnored(e){if(!this.dockerignore||0===this.dockerignore.length)return!1;const t=path.basename(e);for(const s of this.dockerignore){if(s===t||s===e)return!0;if(s.endsWith("/*")&&e.startsWith(s.slice(0,-2)))return!0;if(s.startsWith("*")&&e.endsWith(s.slice(1)))return!0;if(e.includes(`/${s}/`)||e.startsWith(`${s}/`))return!0}return!1}parseSpecFile(e){if(!fs.existsSync(e))throw new Error(`Dockerfile not found at: ${e}`);const t=fs.readFileSync(e,"utf8").split("\n"),s=[];let i="";for(let e of t)if(e=e.trim(),e&&!e.startsWith("#"))if(e.endsWith("\\"))i+=e.slice(0,-1).trim()+" ";else{i+=e;const t=i.indexOf(" ");if(-1!==t){const e=i.slice(0,t).toUpperCase(),n=i.slice(t+1).trim();s.push({instruction:e,args:n,raw:i})}i=""}return s}computeStepHash(e,t,s){return crypto.createHash("sha256").update(`${e}:${t}:${s}`).digest("hex").slice(0,16)}interpolateVars(e,t={}){return e.replace(/\$\{?([a-zA-Z0-9_]+)\}?/g,(e,s)=>void 0!==t[s]?t[s]:e)}async build(e="latest",t="Capsulefile",s={}){let i=path.isAbsolute(t)?t:path.join(this.contextDir,t);if(!fs.existsSync(i)){const e=path.join(this.contextDir,"Dockerfile");fs.existsSync(e)&&(i=e)}const n=this.parseSpecFile(i);console.log(chalk.cyan(`\n[+] Building TNC Capsule from ${path.basename(i)}`)),console.log(chalk.dim(` Context: ${this.contextDir}`)),console.log(chalk.dim(` Total Steps: ${n.length}\n`));const r="img-"+crypto.randomBytes(4).toString("hex"),o={};let a=0,c=null,l=null,h="/",p={};const u=s.buildArgs||{};let g=[],f="node server.js",d="node",m="genesis",y=1;for(const e of n){const t={...u,...p},s=this.interpolateVars(e.args,t),i=this.computeStepHash(m,e.instruction,s),$=`[${y}/${n.length}]`;switch(e.instruction){case"ARG":{let[e,...t]=s.split("=");e=e.trim();const i=t.length>0?t.join("=").trim():"";void 0===u[e]&&(u[e]=i),console.log(chalk.dim(` => ${$} ARG ${e}=${u[e]}`));break}case"FROM":{let e=s;e.startsWith("--platform=")&&(e=e.split(/\s+/).slice(1).join(" "));const t=e.split(/\s+/),i=t[0];let n=null;if(t.length>=3&&"AS"===t[1].toUpperCase()&&(n=t[2]),l){const e=c||String(a);o[e]={workDir:l,env:{...p},workdir:h},c&&(o[String(a)]=o[e]),a++}c=n;const u=n?`stage-${n}`:`stage-${a}`;l=path.join(this.imageManager.baseDir,r,u),fs.ensureDirSync(l),h="/",p={},g=[],d=i.includes("python")?"python":i.includes("rust")?"rust":i.includes("golang")||i.includes("go")?"go":i.includes("cpp")||i.includes("gcc")||i.includes("clang")?"cpp":i.includes("nginx")?"nginx":"node";const f=n?` AS ${n}`:"";if(console.log(chalk.blue(` => ${$} FROM ${chalk.bold(i)}${f}`)),o[i])console.log(chalk.dim(` Using previous stage "${i}" as base...`)),fs.copySync(o[i].workDir,l),p={...o[i].env},h=o[i].workdir;else if("scratch"!==i&&"base"!==i)try{const e=new(require("./oci-puller"));let t=e.inspect(i);if(t||(console.log(chalk.dim(` Pulling base OCI image: ${i}...`)),t=(await e.pull(i)).meta),t&&t.rootfsDir&&fs.existsSync(t.rootfsDir)&&(fs.copySync(t.rootfsDir,l),t.env&&Array.isArray(t.env)))for(const e of t.env){const[t,...s]=e.split("=");p[t]=s.join("=")}}catch(e){console.log(chalk.dim(` Notice: Base OCI pull skipped (${e.message}). Using clean rootfs.`))}break}case"WORKDIR":{h=s.trim();const e=path.join(l,h.replace(/^\//,""));fs.ensureDirSync(e),console.log(chalk.blue(` => ${$} WORKDIR ${h}`));break}case"ENV":{let e,t;if(s.includes("=")){const i=s.indexOf("=");e=s.slice(0,i).trim(),t=s.slice(i+1).trim()}else{const i=s.split(/\s+/);e=i[0],t=i.slice(1).join(" ")}p[e]=t,console.log(chalk.blue(` => ${$} ENV ${e}=${t}`));break}case"COPY":case"ADD":{let e=null,t=s;if(t.startsWith("--from=")){const s=t.indexOf(" ");-1!==s&&(e=t.slice(7,s).trim(),t=t.slice(s+1).trim())}const i=t.split(/\s+/),n=i[0],r=i[1]||".";let a=this.contextDir;if(e){const t=o[e];if(!t)throw new Error(`Stage "${e}" not found in multi-stage build`);a=t.workDir,console.log(chalk.cyan(` => ${$} COPY --from=${e} ${n} -> ${r}`))}else console.log(chalk.blue(` => ${$} COPY ${n} -> ${r}`));const c=path.isAbsolute(n)&&e?path.join(a,n.replace(/^\//,"")):path.join(a,n.replace(/\*/g,"")),p=r.replace(/^\.\//,"").replace(/^\//,"");let u=path.isAbsolute(r)?path.join(l,r.replace(/^\//,"")):path.join(l,h.replace(/^\//,""),p);fs.existsSync(c)&&fs.statSync(c).isFile()&&("."===r||r.endsWith("/")||fs.existsSync(u)&&fs.statSync(u).isDirectory())&&(u=path.join(u,path.basename(c))),fs.existsSync(c)?(fs.ensureDirSync(path.dirname(u)),fs.copySync(c,u,{filter:e=>{if(path.basename(e).startsWith(".git"))return!1;const t=path.relative(a,e);return!this._isIgnored(t)}}),console.log(chalk.green(` [Copied] ${c} -> ${u}`))):console.log(chalk.red(` [Notice] Source path NOT FOUND: ${c}`));break}case"RUN":{let e=s;const t=[];for(;e.startsWith("--mount=");){const s=e.indexOf(" ");if(-1===s)break;const i=e.slice(0,s);t.push(i),e=e.slice(s+1).trim()}for(const e of t)if(e.includes("type=cache")&&e.includes("target=")){const t=e.match(/target=([^,]+)/);if(t&&t[1]){const e=path.join(l,t[1].replace(/^\//,""));fs.ensureDirSync(e)}}const i=path.join(l,h.replace(/^\//,""));fs.ensureDirSync(i),console.log(chalk.yellow(` => ${$} RUN ${chalk.white(e)}`));let n=!1;const{getMicroVMManager:r,sendCommand:o}=require("./microvm-manager"),a=r();if(!a.isTncVzAvailable()||!a.isVMRunning())throw new Error(`[Security Policy] Linux MicroVM is not running. Executing Dockerfile instruction "RUN ${e}" directly on macOS host is blocked for system safety.`);const c=`build-${Date.now().toString(36)}`;try{const t=await a.start(c,{cwd:l,cmd:`cd "${h}" && ${e}`,env:p});if(!t||!t.success)throw new Error(`Failed to initiate build step in MicroVM: ${s}`);const i=12e4,r=Date.now();let u=!0;for(;u&&Date.now()-r<i;){await new Promise(e=>setTimeout(e,600));try{const e=await o({cmd:"ps"},3e3);if(e&&e.ok&&Array.isArray(e.containers)){const t=e.containers.find(e=>e.id===c);u=Boolean(t&&t.running)}else u=!1}catch(e){u=!1}}if(u)throw await a.stop(c).catch(()=>{}),new Error(`Build step timed out after 120s: ${s}`);try{await a.commit(c)}catch(e){console.warn(chalk.yellow(` [Build Commit notice] ${e.message}`))}await a.stop(c).catch(()=>{}),n=!0}catch(e){throw await a.stop(c).catch(()=>{}),console.error(chalk.red(`[FAIL] Build step failed: ${e.message}`)),e}break}case"USER":console.log(chalk.dim(` => ${$} USER ${s}`));break;case"EXPOSE":{const e=parseInt(s.trim(),10);isNaN(e)||g.push(e),console.log(chalk.blue(` => ${$} EXPOSE ${s}`));break}case"CMD":case"ENTRYPOINT":{let t=s.trim();if(t.startsWith("[")&&t.endsWith("]"))try{t=JSON.parse(t).join(" ")}catch(e){}f=t,console.log(chalk.green(` => ${$} ${e.instruction} ${f}`));break}default:console.log(chalk.dim(` => ${$} [Ignored ${e.instruction}] ${s}`))}m=i,y++}const $=path.join(this.imageManager.baseDir,r,"rootfs");l!==$&&(fs.existsSync($)&&fs.removeSync($),fs.moveSync(l,$,{overwrite:!0}));for(let e=0;e<=a;e++){const t=path.join(this.imageManager.baseDir,r,`stage-${e}`);try{fs.removeSync(t)}catch(e){}}for(const e of Object.keys(o))if(o[e]?.workDir&&o[e].workDir!==$)try{fs.removeSync(o[e].workDir)}catch(e){}const k=e.includes(":")?e:`${path.basename(this.contextDir)}:${e}`,S={id:r,tag:k,project:path.basename(this.contextDir),runtimes:[d],entrypoint:f,ports:g,environment:p,workdir:h,builtFrom:"Dockerfile (Multi-Stage)",createdAt:(new Date).toISOString(),sizeMB:this.imageManager.calculateDirSize($)},D=path.join(this.imageManager.baseDir,r);fs.ensureDirSync(D),fs.writeJsonSync(path.join(D,"meta.json"),S,{spaces:2}),fs.writeJsonSync(path.join(D,"blueprint.json"),{project:S.project,runtime:{primary:d,runtimes:[d]},scripts:{start:f},environment:p},{spaces:2});const w=this.imageManager.loadManifest();w[k]=S,w[r]=S,this.imageManager.saveManifest(w);try{const e=path.join(this.imageManager.baseDir,"manifest.json"),t=fs.existsSync(e)?fs.readJsonSync(e):{},s={...S,imageDir:D,rootfsDir:$,layers:n.length};t[k]=s,t[r]=s,fs.writeJsonSync(e,t,{spaces:2})}catch(e){}return console.log(chalk.green(`\n[OK] Successfully built Multi-Stage Capsule: ${chalk.bold(k)} (ID: ${r})`)),console.log(chalk.dim(` Entrypoint: ${f}`)),console.log(chalk.dim(` Exposed: ${g.join(", ")||"none"}\n`)),{success:!0,imageId:r,tag:k,meta:S}}}module.exports=CapsuleBuilder;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const fs=require("fs-extra"),path=require("path"),{execSync}=require("child_process"),TNC_CGROUP_ROOT="/sys/fs/cgroup/tnc";class CgroupsV2Manager{constructor(){this.isLinux="linux"===process.platform,this._v2Available=null}isAvailable(){if(!this.isLinux)return!1;if(null!==this._v2Available)return this._v2Available;try{const t=fs.readFileSync("/sys/fs/cgroup/cgroup.controllers","utf8");this._v2Available=t.includes("memory")&&t.includes("cpu")}catch{this._v2Available=!1}return this._v2Available}cgroupPath(t){return path.join(TNC_CGROUP_ROOT,t)}ensureRootCgroup(){if(!this.isAvailable())return!1;try{fs.ensureDirSync(TNC_CGROUP_ROOT);const t="/sys/fs/cgroup/cgroup.subtree_control",r=fs.readFileSync(t,"utf8"),e=[];r.includes("memory")||e.push("+memory"),r.includes("cpu")||e.push("+cpu"),r.includes("pids")||e.push("+pids"),e.length>0&&fs.writeFileSync(t,e.join(" "));const s=path.join(TNC_CGROUP_ROOT,"cgroup.subtree_control");if(fs.existsSync(s))try{fs.writeFileSync(s,"+memory +cpu +pids")}catch(t){}return!0}catch(t){return!1}}create(t,r={}){if(!this.isAvailable())return{success:!1,reason:"cgroups-v2-unavailable"};const{memoryLimit:e="2g",cpuPercent:s=80,pidsMax:i=500}=r,c=this.cgroupPath(t);try{this.ensureRootCgroup(),fs.ensureDirSync(c);const t=this._parseMemory(e);if(t>0){fs.writeFileSync(path.join(c,"memory.max"),String(t));try{fs.writeFileSync(path.join(c,"memory.swap.max"),"0")}catch(t){}}const r=1e5,n=Math.floor(s/100*r);return fs.writeFileSync(path.join(c,"cpu.max"),`${n} ${r}`),fs.writeFileSync(path.join(c,"pids.max"),String(i)),{success:!0,cgPath:c,memBytes:t,cpuQuota:`${n}/${r} (${s}%)`,pidsMax:i}}catch(t){return{success:!1,reason:t.message}}}assignPid(t,r){if(!this.isAvailable())return!1;try{const e=path.join(this.cgroupPath(t),"cgroup.procs");return fs.writeFileSync(e,String(r)),!0}catch{return!1}}getStats(t){if(!this.isAvailable())return null;const r=this.cgroupPath(t);if(!fs.existsSync(r))return null;const e=(t,e="0")=>{try{return fs.readFileSync(path.join(r,t),"utf8").trim()}catch{return e}},s=parseInt(e("memory.current","0")),i=e("memory.max","max"),c=parseInt(e("pids.current","0")),n=e("pids.max","max");let a=0;try{const t=e("memory.events","").match(/oom_kill\s+(\d+)/);t&&(a=parseInt(t[1]))}catch(t){}let o=0;try{const t=e("cpu.stat","").match(/usage_usec\s+(\d+)/);t&&(o=parseInt(t[1]))}catch(t){}return{memoryMB:Math.round(s/1024/1024),memoryBytes:s,memoryLimitMB:"max"===i?null:Math.round(parseInt(i)/1024/1024),pids:c,pidsLimit:"max"===n?null:parseInt(n),oomKills:a,cpuUsageMs:Math.round(o/1e3)}}cgroupFreeze(t){if(!this.isAvailable())return!1;try{const r=path.join(this.cgroupPath(t),"cgroup.freeze");if(fs.existsSync(r))return fs.writeFileSync(r,"1"),!0}catch(t){}return!1}cgroupThaw(t){if(!this.isAvailable())return!1;try{const r=path.join(this.cgroupPath(t),"cgroup.freeze");if(fs.existsSync(r))return fs.writeFileSync(r,"0"),!0}catch(t){}return!1}killAll(t){if(!this.isAvailable())return!1;const r=this.cgroupPath(t);try{const t=path.join(r,"cgroup.kill");if(fs.existsSync(t))return fs.writeFileSync(t,"1"),!0}catch(t){}try{const t=path.join(r,"cgroup.procs");if(fs.existsSync(t)){const r=fs.readFileSync(t,"utf8").split("\n").map(t=>parseInt(t.trim())).filter(t=>!isNaN(t)&&t>0);for(const t of r)try{process.kill(t,"SIGKILL")}catch(t){}return!0}}catch(t){}return!1}destroy(t){if(!this.isAvailable())return!1;const r=this.cgroupPath(t);try{return this.killAll(t),fs.existsSync(r)&&execSync(`rmdir "${r}" 2>/dev/null || true`,{stdio:"ignore"}),!0}catch{return!1}}_parseMemory(t){if(!t)return 0;const r=String(t).toLowerCase().trim().match(/^(\d+(?:\.\d+)?)([kmg]?)b?$/);if(!r)return 0;const e=parseFloat(r[1]);switch(r[2]){case"k":return Math.floor(1024*e);case"m":return Math.floor(1024*e*1024);case"g":return Math.floor(1024*e*1024*1024);default:return Math.floor(e)}}}module.exports=CgroupsV2Manager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const{spawn,spawnSync}=require("child_process"),fs=require("fs-extra"),path=require("path"),os=require("os");class ContainerMonitor{constructor(t,e){this.sandboxId=t,this.sandboxDir=e||path.join(os.homedir(),".tnc","sandboxes",t),this.logFile=path.join(this.sandboxDir,"output.log"),this.pidFile=path.join(this.sandboxDir,"container.pid"),this.metaFile=path.join(this.sandboxDir,"meta.json")}launch({cmd:t,args:e=[],cwd:s,env:i={},runtime:n="crun"}){fs.ensureDirSync(this.sandboxDir);const r=fs.openSync(this.logFile,"a"),a=spawn(t,e,{cwd:s||this.sandboxDir,env:{...process.env,...i},detached:!0,stdio:["ignore",r,r]}),o=a.pid;return fs.writeFileSync(this.pidFile,String(o),"utf8"),this._updateMeta({pid:o,state:"RUNNING",startedAt:(new Date).toISOString(),exitCode:null,runtime:n}),a.unref(),a.on("exit",(t,e)=>{try{fs.closeSync(r)}catch(t){}this._updateMeta({state:"STOPPED",exitCode:null!==t?t:e?128:0,stoppedAt:(new Date).toISOString()});try{fs.existsSync(this.pidFile)&&fs.removeSync(this.pidFile)}catch(t){}}),{pid:o,logFile:this.logFile}}isAlive(){if(!fs.existsSync(this.pidFile))return!1;try{const t=fs.readFileSync(this.pidFile,"utf8").trim(),e=parseInt(t,10);return!isNaN(e)&&(process.kill(e,0),!0)}catch(t){return!1}}stop(t=5e3){if(!this.isAlive())return this._updateMeta({state:"STOPPED"}),{success:!0,message:"Container already stopped."};try{const e=parseInt(fs.readFileSync(this.pidFile,"utf8").trim(),10);process.kill(e,"SIGTERM");const s=Date.now();for(;Date.now()-s<t;)try{process.kill(e,0),spawnSync("sleep",["0.1"],{stdio:"pipe"})}catch{break}try{process.kill(e,0),process.kill(e,"SIGKILL")}catch(t){}return this._updateMeta({state:"STOPPED",stoppedAt:(new Date).toISOString()}),fs.existsSync(this.pidFile)&&fs.removeSync(this.pidFile),{success:!0}}catch(t){return{success:!1,error:t.message}}}_updateMeta(t={}){try{let e={};fs.existsSync(this.metaFile)&&(e=fs.readJsonSync(this.metaFile)),e={...e,...t,updatedAt:(new Date).toISOString()},fs.writeJsonSync(this.metaFile,e,{spaces:2})}catch(t){}}}module.exports=ContainerMonitor;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),os=require("os"),fs=require("fs-extra"),{spawnSync,spawn}=require("child_process"),https=require("https"),crypto=require("crypto"),chalk=require("chalk"),TNC_BIN_DIR=path.join(os.homedir(),".tnc","bin"),CRUN_LOCAL_BIN=path.join(TNC_BIN_DIR,"crun"),CRUN_VERSION="1.14.4",CRUN_URLS={"linux-amd64":"https://github.com/containers/crun/releases/download/1.14.4/crun-1.14.4-linux-amd64","linux-arm64":"https://github.com/containers/crun/releases/download/1.14.4/crun-1.14.4-linux-arm64"},CRUN_SHA256={"linux-amd64":"4f170aaa10d2ef02560cfb60b67ddfa1a83b1b4f7018227e9cb23a6af3955ec1","linux-arm64":"308f8719055de178897f66cbb72d6a02567050ac645dd5eca52f48de347dda6c"};class CrunManager{constructor(){this.binDir=TNC_BIN_DIR,this.crunPath=this._locateCrun(),fs.ensureDirSync(this.binDir)}_locateCrun(){if(fs.existsSync(CRUN_LOCAL_BIN))try{if(fs.statSync(CRUN_LOCAL_BIN).size>1e5)return CRUN_LOCAL_BIN}catch(t){}try{const t=spawnSync("which",["crun"],{stdio:"pipe"});if(0===t.status)return t.stdout.toString().trim()}catch(t){}return null}isAvailable(){return Boolean(this.crunPath&&fs.existsSync(this.crunPath))}getVersion(){if(!this.isAvailable())return null;try{const t=spawnSync(this.crunPath,["--version"],{stdio:"pipe"});if(0===t.status)return t.stdout.toString().split("\n")[0].replace(/^crun version\s+/,"").trim()}catch(t){}return"unknown"}async ensureCrun(){if(this.isAvailable())return this.crunPath;if("linux"!==process.platform)return null;const t="arm64"===process.arch?"arm64":"amd64",r=`linux-${t}`,n=CRUN_URLS[r];if(!n)throw new Error(`Unsupported architecture for static crun download: ${t}`);console.log(chalk.cyan(`\n[crun] Embedding official static OCI runtime (crun v1.14.4 ${t})...`)),console.log(chalk.dim(` Download: ${n}`)),console.log(chalk.dim(` Target: ${CRUN_LOCAL_BIN}`));const e=`${CRUN_LOCAL_BIN}.download`;fs.mkdirSync(this.binDir,{recursive:!0}),await this._downloadFile(n,e);const s=crypto.createHash("sha256").update(fs.readFileSync(e)).digest("hex");if(s!==CRUN_SHA256[r])throw fs.rmSync(e,{force:!0}),new Error(`crun checksum mismatch for ${r}: expected ${CRUN_SHA256[r]}, got ${s}`);fs.renameSync(e,CRUN_LOCAL_BIN),fs.chmodSync(CRUN_LOCAL_BIN,493),this.crunPath=CRUN_LOCAL_BIN;const c=this.getVersion();return console.log(chalk.green(`✓ [crun] Successfully installed embedded OCI runtime (version ${c})!\n`)),this.crunPath}_downloadFile(t,r){return new Promise((n,e)=>{const s=t=>{https.get(t,t=>{if(t.statusCode>=300&&t.statusCode<400&&t.headers.location)return s(t.headers.location);if(200!==t.statusCode)return e(new Error(`Failed to download crun: HTTP ${t.statusCode}`));const c=fs.createWriteStream(r);t.pipe(c),c.on("finish",()=>{c.close(),n()}),c.on("error",e)}).on("error",e)};s(t)})}create(t,r,n={}){const e=this.crunPath||"crun";return spawnSync(e,["create","--bundle",r,t],{stdio:"pipe"})}start(t){const r=this.crunPath||"crun";return spawnSync(r,["start",t],{stdio:"pipe"})}run(t,r,n="inherit"){const e=this.crunPath||"crun";return spawn(e,["run","--bundle",r,t],{detached:!0,stdio:n})}kill(t,r="SIGTERM"){const n=this.crunPath||"crun";return spawnSync(n,["kill",t,r],{stdio:"pipe"})}delete(t,r=!0){const n=this.crunPath||"crun",e=["delete"];return r&&e.push("--force"),e.push(t),spawnSync(n,e,{stdio:"pipe"})}state(t){const r=this.crunPath||"crun";try{const n=spawnSync(r,["state",t],{stdio:"pipe"});if(0===n.status)return JSON.parse(n.stdout.toString())}catch(t){}return null}exec(t,r=["/bin/sh"],n={}){const e=this.crunPath||"crun",s=["exec"];return n.tty&&s.push("-t"),n.interactive&&s.push("-i"),s.push(t,...r),spawn(e,s,{stdio:n.stdio||"inherit"})}}module.exports=CrunManager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const{spawn,execSync}=require("child_process"),path=require("path"),chalk=require("chalk");let fs;try{fs=require("fs-extra")}catch(e){try{fs=require("/Users/ramansingh/ThinkNCollab-Shell-V2/node_modules/fs-extra")}catch(e){const t=require("fs");fs={...t,ensureDirSync:e=>t.mkdirSync(e,{recursive:!0}),readJsonSync:e=>JSON.parse(t.readFileSync(e,"utf8")),writeJsonSync:(e,s)=>t.writeFileSync(e,JSON.stringify(s,null,2),"utf8")}}}const os=require("os"),SandboxNetwork=require("./network"),SandboxStorage=require("./storage"),MacOSSandbox=require("./macos-sandbox"),CgroupsV2=require("./cgroups"),WindowsWSL2=require("./windows-wsl2"),SandboxSupervisor=require("./supervisor"),RuntimeManager=require("./runtime-manager"),CrunManager=require("./crun-manager"),OCIRuntimeSpec=require("./oci-runtime-spec"),ContainerMonitor=require("./container-monitor");let getMicroVMManager=null;try{getMicroVMManager=require("./microvm-manager").getMicroVMManager}catch(e){}let getDepStore=null;try{getDepStore=require("../dep-store").getDepStore}catch(e){}class SandboxEngine{constructor(e={}){if(this.baseDir=e.baseDir||path.join(os.homedir(),".tnc","sandboxes"),fs.ensureDirSync(this.baseDir),this.network=new SandboxNetwork,this.cgroups=new CgroupsV2,this.wsl2=new WindowsWSL2,this.crun=new CrunManager,this.supervisor=new SandboxSupervisor(this),!1!==e.autoCleanup)try{this.selfHealingCleanup()}catch(e){}}selfHealingCleanup(){const e={staleSocketsRemoved:0,stalePidsRemoved:0,deadContainersMarked:0},t=path.join(os.homedir(),".tnc","microvm.pid"),s=path.join(os.homedir(),".tnc","vmctl.sock");let r=!1;if(fs.existsSync(t))try{const e=parseInt(fs.readFileSync(t,"utf8").trim(),10);e&&(process.kill(e,0),r=!0)}catch(r){try{fs.removeSync(t),e.stalePidsRemoved++}catch(e){}if(fs.existsSync(s))try{fs.removeSync(s),e.staleSocketsRemoved++}catch(e){}}else if(fs.existsSync(s))try{fs.removeSync(s),e.staleSocketsRemoved++}catch(e){}if(fs.existsSync(this.baseDir))try{const t=fs.readdirSync(this.baseDir);for(const s of t){const t=path.join(this.baseDir,s,"meta.json");if(fs.existsSync(t))try{const s=fs.readJsonSync(t);if(s&&"RUNNING"===s.state){let n=!1;if("macos-microvm-vz"===s.isolationLevel||"linux-microvm-vz"===s.isolationLevel)r||(n=!0);else if(s.pid)try{process.kill(s.pid,0)}catch(e){n=!0}n&&(s.state="STOPPED",s.health="DEAD",s.pid=null,s.stoppedAt=(new Date).toISOString(),fs.writeJsonSync(t,s,{spaces:2}),e.deadContainersMarked++)}}catch(e){}}}catch(e){}return e}async _findCrun(){if(this.crun.isAvailable())return this.crun.crunPath;if("linux"===process.platform)try{return await this.crun.ensureCrun()}catch(e){}return null}getMetaPath(e){return path.join(this.baseDir,e,"meta.json")}loadMeta(e){const t=this.getMetaPath(e);if(fs.existsSync(t))try{return fs.readJsonSync(t)}catch(e){}return null}saveMeta(e,t){const s=this.getMetaPath(e);fs.ensureDirSync(path.dirname(s)),fs.writeJsonSync(s,t,{spaces:2})}async create(e,t={}){let s=t.cwd||process.cwd(),r=t.cmd||"node server.js",n=t.hostPort||null,o=t.containerPort||null,i=null;if(t.image)try{const e=new(require("./oci-puller"));if(i=e.inspect(t.image),i||(i=(await e.pull(t.image)).meta),i){if(i.rootfsDir&&fs.existsSync(i.rootfsDir)&&(s=i.rootfsDir),"node server.js"===r&&(i.entrypoint?.length||i.cmd?.length)){const e=i.entrypoint||[],t=i.cmd||[];r=[...e,...t].join(" ")}if(!o&&i.exposedPorts){const e=Object.keys(i.exposedPorts)[0];if(e){const t=parseInt(e.split("/")[0],10);isNaN(t)||(o=t,n||(n=t))}}}}catch(e){console.warn(`[TNC Engine] Create image resolution notice: ${e.message}`)}new SandboxStorage(e).initStorage(null,s);const a=path.join(this.baseDir,e,"bundle");fs.ensureDirSync(a);const c=OCIRuntimeSpec.generate({rootfsPath:s,cmd:Array.isArray(r)?r:r.split(" "),workingDir:s,env:Object.entries(t.env||{}).map(([e,t])=>`${e}=${t}`)});fs.writeJsonSync(path.join(a,"config.json"),c,{spaces:2});const l={id:e,name:t.name||e,image:t.image||null,state:"CREATED",pid:null,pgid:null,cmd:r,cwd:s,hostPort:n,containerPort:o,memoryLimit:t.memoryLimit||"2g",environment:t.env||{},ephemeral:t.ephemeral||!1,health:"STARTING",createdAt:(new Date).toISOString(),startedAt:null,frozenAt:null};return this.saveMeta(e,l),{success:!0,message:`Container "${e}" created.`,meta:l}}async start(e,t={}){let s=this.loadMeta(e);s&&"CREATED"===s.state&&(t={cmd:s.cmd,cwd:s.cwd,hostPort:s.hostPort,containerPort:s.containerPort,memoryLimit:s.memoryLimit,image:s.image,env:s.environment||{},name:s.name,...t});let{cmd:r="node server.js",cwd:n=process.cwd(),hostPort:o=null,containerPort:i=null,memoryLimit:a="2g",ephemeral:c=!1,nodeVersion:l=null}=t,u=s;if(u&&u.pid)try{process.kill(u.pid,0);let s=!1;return t.roomId&&u.roomId!==t.roomId&&(u.roomId=t.roomId,s=!0),t.roomName&&u.roomName!==t.roomName&&(u.roomName=t.roomName,s=!0),t.userId&&u.userId!==t.userId&&(u.userId=t.userId,s=!0),t.userName&&u.userName!==t.userName&&(u.userName=t.userName,s=!0),s&&this.saveMeta(e,u),{success:!1,message:`Sandbox "${e}" is already running with PID ${u.pid}`,meta:u}}catch{}let d=null,m=null;if(t.image)try{const e=new(require("./oci-puller"));if(m=e.inspect(t.image),m||(m=(await e.pull(t.image)).meta),m){if(m.rootfsDir&&fs.existsSync(m.rootfsDir)&&(n=m.rootfsDir),"node server.js"===r&&(m.entrypoint?.length||m.cmd?.length)){const e=m.entrypoint||[],t=m.cmd||[];r=[...e,...t].join(" ")}if(!i&&m.exposedPorts){const e=Object.keys(m.exposedPorts)[0];if(e){const t=parseInt(e.split("/")[0],10);isNaN(t)||(i=t,o||(o=t))}}try{const e=new(require("./arch-shim")),t=m.platform?m.platform.split("/")[1]:"amd64",s=await e.getExecWrapper(t);s.wrapper&&s.wrapper.length>0&&(r=`${s.wrapper.join(" ")} ${r}`)}catch(e){}}}catch(e){console.warn(`[TNC Engine] Image resolution notice: ${e.message}`)}try{const e=l||t.runtime&&t.runtime.node;d=await RuntimeManager.ensureNodeRuntime(n,e)}catch(e){console.warn(`[TNC Runtime] Runtime resolution notice: ${e.message}`)}const p=d?d.binDir:null,h=new SandboxStorage(e);h.initStorage(null,n,d?.runtimeDir);const f="linux"===process.platform;let y,g="host-process",S=2048;const w="10.89.0."+(Math.floor(200*Math.random())+10),v=path.join(this.baseDir,e,"output.log");fs.ensureDirSync(path.dirname(v));const N=f?fs.openSync(v,"a"):null;if(f){this.network.setupBridge();const t=fs.existsSync(path.join(h.mergedDir,"bin","sh"))||fs.existsSync(path.join(h.mergedDir,"bin","bash")),s=p?`export PATH="/opt/node/bin:${p}:$PATH"; `:"",c=t?["-m","-p","-n","-u","-i","-f","chroot",h.mergedDir,"/bin/sh","-c",`mount -t proc proc /proc 2>/dev/null || true; ${s}cd /workspace && exec ${r}`]:["-m","-p","-n","-u","-i","-f","/bin/sh","-c",`${s}cd "${n}" && exec ${r}`],l=await this._findCrun();if(l)try{const s=path.join(this.baseDir,e,"bundle");fs.ensureDirSync(s);const o=t?h.mergedDir:n,i=OCIRuntimeSpec.generate({rootfsPath:o,cmd:Array.isArray(r)?r:r.split(" "),workingDir:t?"/workspace":n,mounts:[{destination:"/workspace",type:"bind",source:n,options:["rbind","rw"]}]});fs.writeJsonSync(path.join(s,"config.json"),i,{spaces:2});y={pid:new ContainerMonitor(e,path.join(this.baseDir,e)).launch({cmd:l,args:["run","--bundle",s,e],cwd:s,runtime:"crun"}).pid},g="linux-crun-oci"}catch(e){console.warn(`[TNC Engine] crun launch notice, falling back to unshare: ${e.message}`)}y||(y=spawn("unshare",c,{detached:!0,stdio:["ignore",N,N],env:{...process.env,PATH:p?`${p}:${process.env.PATH}`:process.env.PATH}}),g="linux-unshare"),this.network.attachNetwork(e,y.pid,w),o&&i&&this.network.mapPort(o,w,i);const u=this.cgroups.create(e,{memoryLimit:a,cpuPercent:80,pidsMax:500});u.success&&(this.cgroups.assignPid(e,y.pid),g="linux-unshare+cgroups2",S=Math.round(u.memBytes/1024/1024))}else if("darwin"===process.platform){if(getMicroVMManager){let s=null;try{const a=getMicroVMManager(),c=await a.getStatus();(c.available||c.kernelReady)&&(s=await a.start(e,{image:t.image,cmd:r,cwd:n,hostPort:o,containerPort:i,env:t.env||{},volumes:t.volumes||[],name:t.name,roomId:t.roomId,roomName:t.roomName,userId:t.userId,userName:t.userName}))}catch(e){if(t.image)throw e;console.warn(chalk.yellow(`[TNC Engine] MicroVM (${e.message}) — falling back to sandbox-exec`))}if(s&&s.success){const t=s.meta;return this.saveMeta(e,{...t,isolationLevel:"macos-microvm-vz"}),{success:!0,meta:t}}}const s=(new MacOSSandbox).spawnSandboxed(e,r,{cwd:n,hostPort:o,containerPort:i,memoryLimit:a,allowNetwork:!0,outFd:N,runtimeBinDir:p});y=s.child,g=s.isolationLevel,S=s.memoryLimitMB}else if("win32"===process.platform){const t=this.wsl2.spawnInWSL2(e,r,{cwd:n,hostPort:o,containerPort:i,memoryLimit:a,outFd:N,runtimeBinDir:p});if(t.success)y=t.child,g=t.isolationLevel,S=t.memoryLimitMB;else{const t=process.env.PATH||process.env.Path||"",s=p?`${p};${t}`:t;y=spawn("cmd.exe",["/s","/c",r],{cwd:n,detached:!0,stdio:["ignore",N,N],env:{...process.env,TNC_SANDBOX_ID:e,TNC_PLATFORM:"host-process-win32",PORT:String(o||i||"3000"),PATH:s,Path:s}}),g="host-process"}}else y=spawn("/bin/sh",["-c",r],{cwd:n,detached:!0,stdio:["ignore",N,N],env:{...process.env,TNC_SANDBOX_ID:e,PORT:String(o||i||3e3),PATH:p?`${p}:${process.env.PATH}`:process.env.PATH}}),g="host-process";y.unref();const D=y.pid;if(s=this.loadMeta(e),u={id:e,name:t.name||s&&s.name||e,image:t.image||s&&s.image||null,roomId:t.roomId||s&&s.roomId||process.env.THINKNCOLLAB_ROOM_ID||null,roomName:t.roomName||s&&s.roomName||process.env.THINKNCOLLAB_ROOM_NAME||null,userId:t.userId||s&&s.userId||process.env.THINKNCOLLAB_USER_ID||null,userName:t.userName||s&&s.userName||process.env.THINKNCOLLAB_USER_NAME||null,state:"RUNNING",pid:y.pid,pgid:D,cmd:r,cwd:n,hostPort:o,containerPort:i,containerIp:w,memoryLimit:a,isolationLevel:g,memoryLimitMB:S,ephemeral:c,health:"HEALTHY",healthPath:t.healthPath||"/health",cpuPercent:0,memoryMB:0,restartCount:s&&s.restartCount||0,runtime:d?{node:d.version,binDir:d.binDir,nodePath:d.nodePath,isHermetic:!0}:{node:process.versions.node,isHost:!0},startedAt:(new Date).toISOString(),frozenAt:null},this.saveMeta(e,u),this.supervisor.watch(e,u),getDepStore)try{const t=new(require("../dependency-tracker"))(n).loadConfig();getDepStore().recordFromConfig(t,{sandboxId:e})}catch(e){}const x=t.standalone||t.noTunnel||"true"===process.env.TNC_STANDALONE||"true"===process.env.THINKNCOLLAB_STANDALONE;return o&&!x&&this.startTunnelDaemon(e,o,t.serverUrl,t.userId),{success:!0,message:`Sandbox "${e}" started successfully.`,meta:u}}async freeze(e){const t=this.loadMeta(e);if(!t)return{success:!1,message:`Sandbox "${e}" does not exist.`};if("FROZEN"===t.state)return{success:!0,message:`Sandbox "${e}" is already frozen.`,meta:t};if(this.supervisor.unwatch(e),this.stopTunnelDaemon(e),"macos-microvm-vz"===t.isolationLevel||"linux-microvm-vz"===t.isolationLevel){if(getMicroVMManager)try{await getMicroVMManager().stop(e)}catch(e){}return t.state="STOPPED",t.frozenPid=t.pid,t.pid=null,t.frozenAt=(new Date).toISOString(),this.saveMeta(e,t),{success:!0,message:`Container "${e}" stopped. RAM released to 0 MB.`,meta:t}}if(t.pid){const s=t.pgid||t.pid;if("linux-crun-oci"===t.isolationLevel)try{new(require("./container-monitor"))(e,path.join(this.baseDir,e)).stop()}catch(e){}"linux"===process.platform&&(this.cgroups.killAll(e),await new Promise(e=>setTimeout(e,500))),"win32"===process.platform&&(this.wsl2.killSandbox(t.pid,e),this.wsl2.freezeCleanup(t),await new Promise(e=>setTimeout(e,500)));try{process.kill(-s,"SIGTERM")}catch(e){try{process.kill(t.pid,"SIGTERM")}catch(e){}}await new Promise(e=>setTimeout(e,1500));try{process.kill(-s,0);try{process.kill(-s,"SIGKILL")}catch(e){}}catch(e){}if("win32"!==process.platform)try{execSync(`kill -9 -${s} 2>/dev/null || true`,{stdio:"ignore"})}catch(e){}if(t.hostPort&&t.containerIp&&t.containerPort&&this.network.unmapPort(t.hostPort,t.containerIp,t.containerPort),"darwin"===process.platform&&t.hostPort&&t.containerPort)try{(new(require("./macos-sandbox"))).removePortForward(t.hostPort,t.containerPort)}catch(e){}}return t.state="FROZEN",t.frozenPid=t.pid,t.pid=null,t.frozenAt=(new Date).toISOString(),this.saveMeta(e,t),this.supervisor.unwatch(e),{success:!0,message:`Sandbox "${e}" frozen. RAM dropped to 0 MB. Filesystem preserved.`,meta:t}}async stop(e){return this.freeze(e)}async resume(e){const t=this.loadMeta(e);if(!t)return{success:!1,message:`Sandbox "${e}" does not exist.`};if("RUNNING"===t.state&&t.pid)try{return process.kill(t.pid,0),{success:!0,message:`Sandbox "${e}" is already running.`,meta:t}}catch(e){}return await this.start(e,{cmd:t.cmd,cwd:t.cwd,hostPort:t.hostPort,containerPort:t.containerPort,memoryLimit:t.memoryLimit,ephemeral:t.ephemeral,name:t.name,roomId:t.roomId,roomName:t.roomName,userId:t.userId,userName:t.userName,nodeVersion:t.runtime&&t.runtime.node||null})}async destroy(e){return this.supervisor.unwatch(e),await this.freeze(e),"linux"===process.platform&&this.cgroups.destroy(e),new SandboxStorage(e).purge(),{success:!0,message:`Sandbox "${e}" destroyed and cleaned up.`}}list(){const e=[],t=new Set;let s=null;const r=()=>{if(null!==s)return s;s=new Set;try{const e=path.join(os.homedir(),".tnc","vmctl.sock");if(!fs.existsSync(e))return s;const t=['const net=require("net"),sock=new net.Socket();','let d="";',"sock.setTimeout(3000);",'sock.on("data",b=>{d+=b;if(d.includes("\\n")){process.stdout.write(d);sock.destroy();}});','sock.on("timeout",()=>{process.stdout.write("{}\\n");sock.destroy();});','sock.on("error",()=>process.stdout.write("{}\\n"));',`sock.connect(${JSON.stringify(e)});`,'sock.write(\'{"cmd":"ps"}\\n\');'].join(""),{execFileSync:r}=require("child_process"),n=r(process.execPath,["-e",t],{timeout:4e3,encoding:"utf8"}).trim().split("\n").pop(),o=JSON.parse(n);if(o&&o.ok&&Array.isArray(o.containers))for(const e of o.containers)e.id&&!1!==e.running&&s.add(e.id)}catch(e){}return s};if(fs.existsSync(this.baseDir)){const s=fs.readdirSync(this.baseDir);for(const n of s){const s=this.loadMeta(n);if(s){if(t.add(n),"RUNNING"===s.state&&s.pid)if("macos-microvm-vz"===s.isolationLevel||"linux-microvm-vz"===s.isolationLevel){const e=path.join(os.homedir(),".tnc","vmctl.sock");if(fs.existsSync(e))if(r().has(n))s.health="HEALTHY";else{s.state="DEAD",s.health="DEAD";try{const e=path.join(this.baseDir,n,"meta.json");if(fs.existsSync(e)){const t=fs.readJsonSync(e);t.state="DEAD",t.health="DEAD",fs.writeJsonSync(e,t,{spaces:2})}}catch(e){}}else s.state="DEAD",s.health="DEAD"}else try{process.kill(s.pid,0);const e=this.supervisor.gatherTelemetry(s.pid);s.cpuPercent=e.cpu,s.memoryMB=e.memory,s.health=s.health||"HEALTHY"}catch{s.state="DEAD",s.health="DEAD"}e.push(s)}}}const n=path.join(os.homedir(),".tnc","state");if(fs.existsSync(n))try{const s=r(),o=fs.readdirSync(n);for(const r of o){if(!r.endsWith(".json"))continue;const o=path.basename(r,".json");if(!t.has(o))try{const i=fs.readJsonSync(path.join(n,r));if(i){const r=s.size>0?s.has(o):Boolean(i.running);e.push({id:i.id||o,name:i.id||o,image:"oci-container",state:r?"RUNNING":"DEAD",pid:i.pid,vmIp:i.ip,isolationLevel:"macos-microvm-vz",health:r?"HEALTHY":"DEAD",startedAt:(new Date).toISOString()}),t.add(o)}}catch(e){}}}catch(e){}return e}startTunnelDaemon(e,t,s=null,r=null){try{this.stopTunnelDaemon(e);const n=path.join(__dirname,"tunnel-daemon.js"),o=s||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com",i=r||process.env.THINKNCOLLAB_USER_ID||"",a=spawn(process.execPath,[n,e,String(t),o,i],{detached:!0,stdio:"ignore"});a.unref();const c=path.join(this.baseDir,e,"tunnel.pid");fs.writeFileSync(c,String(a.pid))}catch(e){}}stopTunnelDaemon(e){try{const t=path.join(this.baseDir,e,"tunnel.pid");if(fs.existsSync(t)){const e=parseInt(fs.readFileSync(t,"utf8").trim(),10);if(e&&!isNaN(e))try{process.kill(e,"SIGTERM")}catch(e){}try{fs.removeSync(t)}catch(e){}}}catch(e){}}async kill(e,t="SIGKILL"){const s=this.loadMeta(e);if(!s)return{success:!1,message:`Container "${e}" does not exist.`};if(this.supervisor.unwatch(e),this.stopTunnelDaemon(e),"linux-crun-oci"===s.isolationLevel&&this.crun.isAvailable())try{this.crun.kill(e,t)}catch(e){}if(s.pid){const e=s.pgid||s.pid;try{process.kill(-e,t)}catch(e){try{process.kill(s.pid,t)}catch(e){}}if("win32"!==process.platform)try{execSync(`kill -9 -${e} 2>/dev/null || true`,{stdio:"ignore"})}catch(e){}}return s.state="STOPPED",s.exitCode=137,s.stoppedAt=(new Date).toISOString(),this.saveMeta(e,s),{success:!0,message:`Container "${e}" killed (${t}).`,meta:s}}logs(e,t={}){const s=path.join(this.baseDir,e,"output.log");if(!fs.existsSync(s))return"";try{const e=fs.readFileSync(s,"utf8");return t.tail&&"number"==typeof t.tail?e.trim().split("\n").slice(-t.tail).join("\n"):e}catch(e){return""}}async exec(e,t=["/bin/sh"],s={}){const r=this.loadMeta(e);if(!r||"RUNNING"!==r.state)throw new Error(`Container "${e}" is not running.`);const{spawnSync:n}=require("child_process"),o=s.cwd||r.cwd||process.cwd(),i={...process.env,...r.environment,...s.env,TNC_CONTAINER_ID:e};if("macos-microvm-vz"===r.isolationLevel||"linux-microvm-vz"===r.isolationLevel){const{getMicroVMManager:s}=require("./microvm-manager"),r=s(),n=(Array.isArray(t)?t:t.split(" ")).map(e=>e.includes(" ")||e.includes("\t")||e.includes(";")||e.includes('"')||e.includes("$")?`"${e.replace(/(["\\$`])/g,"\\$1")}"`:e).join(" "),o=await r.exec(e,n);return o&&o.ok?{status:0,stdout:o.output||"",stderr:""}:{status:1,stdout:"",stderr:o?.error||"Exec error"}}if("linux-crun-oci"===r.isolationLevel&&this.crun.isAvailable())return this.crun.exec(e,Array.isArray(t)?t:t.split(" "),s);if("linux"===process.platform&&r.pid)try{const e=Array.isArray(t)?t:t.split(" ");return n("nsenter",["-t",String(r.pid),"-m","-u","-i","-n","-p","--",...e],{stdio:s.stdio||"pipe",env:i,cwd:o,encoding:"utf8"})}catch(e){}return n("/bin/sh",["-c",Array.isArray(t)?t.join(" "):t],{stdio:s.stdio||"pipe",cwd:o,env:i,encoding:"utf8"})}inspect(e){const t=this.loadMeta(e);if(!t)return null;if("RUNNING"===t.state&&t.pid)try{process.kill(t.pid,0);const e=this.supervisor.gatherTelemetry(t.pid);t.cpuPercent=e.cpu,t.memoryMB=e.memory,t.isAlive=!0}catch{t.isAlive=!1,t.state="DEAD"}return t}async prune(){const e=this.list(),t=[];for(const s of e)"RUNNING"!==s.state&&(await this.destroy(s.id),t.push(s.id));return{prunedCount:t.length,pruned:t}}}module.exports=SandboxEngine;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),fs=require("fs-extra"),os=require("os"),{execSync}=require("child_process"),DependencyTracker=require("../dependency-tracker");class ImageManager{constructor(e=null){this.baseDir=e||path.join(os.homedir(),".tnc","images"),fs.ensureDirSync(this.baseDir),this.manifestFile=path.join(this.baseDir,"images.json"),this.initStore()}initStore(){fs.existsSync(this.manifestFile)||fs.writeJsonSync(this.manifestFile,{})}loadManifest(){try{return fs.readJsonSync(this.manifestFile)}catch{return{}}}saveManifest(e){fs.writeJsonSync(this.manifestFile,e,{spaces:2})}async build(e="latest",t=process.cwd(),s={}){const n=new DependencyTracker(t),i=await n.scanAndRecord()||n.loadConfig(),r="img-"+Math.random().toString(36).substring(2,10),a=e.includes(":")?e:`${i.project||"app"}:${e}`,o=path.join(this.baseDir,r);fs.ensureDirSync(o),fs.writeJsonSync(path.join(o,"blueprint.json"),i);const c={id:r,tag:a,project:i.project,runtimes:i.runtime?.runtimes||[i.runtime?.primary||"node"],dependenciesCount:Object.values(i.dependencies||{}).reduce((e,t)=>e+(t?.length||0),0),sizeMB:this.calculateDirSize(t),createdAt:(new Date).toISOString(),author:s.author||os.userInfo().username,entrypoint:i.scripts?.start||i.scripts?.dev||"node server.js",environment:i.environment||{}};fs.writeJsonSync(path.join(o,"meta.json"),c);const d=this.loadManifest();return d[a]=c,d[r]=c,this.saveManifest(d),{success:!0,imageId:r,tag:a,meta:c}}list(){const e=this.loadManifest(),t=new Map;for(const[s,n]of Object.entries(e))t.has(n.id)||t.set(n.id,n);return Array.from(t.values())}inspect(e){return this.loadManifest()[e]||null}tag(e,t){const s=this.loadManifest(),n=s[e];if(!n)throw new Error(`Image "${e}" not found.`);const i={...n,tag:t,updatedAt:(new Date).toISOString()};return s[t]=i,this.saveManifest(s),i}remove(e,t=!1){const s=this.loadManifest(),n=s[e];if(!n)throw new Error(`Image "${e}" not found.`);const i=n.id;for(const[t,n]of Object.entries(s))n.id!==i&&t!==e||delete s[t];this.saveManifest(s);const r=path.join(this.baseDir,i);return fs.existsSync(r)&&fs.removeSync(r),{success:!0,message:`Image "${e}" removed.`}}save(e,t){const s=this.inspect(e);if(!s)throw new Error(`Image "${e}" not found.`);path.join(this.baseDir,s.id);const n=path.resolve(t||`${s.tag.replace(/[:/]/g,"-")}.tar.gz`);return execSync(`tar -czf "${n}" -C "${this.baseDir}" "${s.id}"`),{success:!0,file:n,size:(fs.statSync(n).size/1048576).toFixed(2)+" MB"}}load(e){const t=path.resolve(e);if(!fs.existsSync(t))throw new Error(`File "${e}" not found.`);execSync(`tar -xzf "${t}" -C "${this.baseDir}"`);const s=fs.readdirSync(this.baseDir),n=this.loadManifest();let i=null;for(const e of s){const t=path.join(this.baseDir,e,"meta.json");if(fs.existsSync(t))try{const e=fs.readJsonSync(t);n[e.tag]=e,n[e.id]=e,i=e}catch(e){}}return this.saveManifest(n),{success:!0,meta:i}}prune(){this.loadManifest();let e=0;const t=new Set(this.list().map(e=>e.id)),s=fs.readdirSync(this.baseDir).filter(e=>e.startsWith("img-"));for(const n of s)t.has(n)||(fs.removeSync(path.join(this.baseDir,n)),e++);return{success:!0,deletedCount:e}}calculateDirSize(e){try{const t=execSync(`du -sm "${e}" 2>/dev/null || echo "10"`,{encoding:"utf8"}).trim();return parseInt(t.split(/\s+/)[0],10)||10}catch{return 10}}}module.exports=ImageManager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),os=require("os"),fs=require("fs-extra"),crypto=require("crypto"),DEFAULT_OCI_DIR=path.join(os.homedir(),".tnc","oci");class ImageStore{constructor(e=DEFAULT_OCI_DIR){this.baseDir=e,this.blobsDir=path.join(this.baseDir,"blobs","sha256"),this.rootfsBaseDir=path.join(this.baseDir,"rootfs"),this.dbPath=path.join(this.baseDir,"index.db"),fs.ensureDirSync(this.blobsDir),fs.ensureDirSync(this.rootfsBaseDir),this.db=this._initDatabase()}_initDatabase(){let e=null;try{const{DatabaseSync:t}=require("node:sqlite");return e=new t(this.dbPath),e.exec("\n CREATE TABLE IF NOT EXISTS images (\n id TEXT PRIMARY KEY,\n repo TEXT NOT NULL,\n tag TEXT NOT NULL,\n digest TEXT,\n platform TEXT,\n size_bytes INTEGER DEFAULT 0,\n layers_count INTEGER DEFAULT 0,\n pulled_at TEXT,\n last_used_at TEXT,\n rootfs_dir TEXT,\n config_json TEXT\n );\n\n CREATE UNIQUE INDEX IF NOT EXISTS idx_repo_tag ON images(repo, tag);\n\n CREATE TABLE IF NOT EXISTS blobs (\n digest TEXT PRIMARY KEY,\n size_bytes INTEGER DEFAULT 0,\n media_type TEXT,\n created_at TEXT\n );\n\n CREATE TABLE IF NOT EXISTS image_blobs (\n image_id TEXT NOT NULL,\n blob_digest TEXT NOT NULL,\n PRIMARY KEY (image_id, blob_digest)\n );\n "),e}catch(e){return this._initJsonFallback()}}_initJsonFallback(){const e=path.join(this.baseDir,"index.json");let t={images:{},blobs:{},image_blobs:[]};if(fs.existsSync(e))try{t=fs.readJsonSync(e)}catch(e){}const s=()=>{try{fs.writeJsonSync(e,t,{spaces:2})}catch(e){}};return{isFallback:!0,exec:()=>{},prepare:e=>({run:(...i)=>{if(e.includes("INSERT INTO blobs")||e.includes("INSERT OR REPLACE INTO blobs")){const[e,r,a,o]=i;t.blobs[e]={digest:e,size_bytes:r,media_type:a,created_at:o},s()}else if(e.includes("INSERT INTO images")||e.includes("INSERT OR REPLACE INTO images")){const[e,r,a,o,n,l,c,d,b,g,E]=i;t.images[`${r}:${a}`]={id:e,repo:r,tag:a,digest:o,platform:n,size_bytes:l,layers_count:c,pulled_at:d,last_used_at:b,rootfs_dir:g,config_json:E},s()}else if(e.includes("INSERT OR IGNORE INTO image_blobs")){const[e,r]=i;t.image_blobs.some(t=>t.image_id===e&&t.blob_digest===r)||(t.image_blobs.push({image_id:e,blob_digest:r}),s())}else if(e.includes("DELETE FROM images WHERE id = ?")){const[e]=i;for(const[s,i]of Object.entries(t.images))i.id===e&&delete t.images[s];t.image_blobs=t.image_blobs.filter(t=>t.image_id!==e),s()}else if(e.includes("DELETE FROM blobs WHERE digest = ?")){const[e]=i;delete t.blobs[e],s()}},all:(...s)=>{if(e.includes("SELECT * FROM images WHERE repo = ? AND tag = ?")){const[e,i]=s,r=t.images[`${e}:${i}`];return r?[r]:[]}if(e.includes("SELECT * FROM images WHERE id = ?")){const[e]=s,i=Object.values(t.images).find(t=>t.id===e);return i?[i]:[]}if(e.includes("SELECT * FROM images ORDER BY"))return Object.values(t.images);if(e.includes("SELECT * FROM blobs"))return Object.values(t.blobs);if(e.includes("SELECT DISTINCT blob_digest FROM image_blobs")){const e=new Set(t.image_blobs.map(e=>e.blob_digest));return Array.from(e).map(e=>({blob_digest:e}))}return[]}})}}getBlobPath(e){const t=e.replace(/^sha256:/,"");return path.join(this.blobsDir,t)}hasBlob(e){const t=this.getBlobPath(e);if(!fs.existsSync(t))return!1;try{return fs.statSync(t).size>0}catch(e){return!1}}putBlob(e,t,s="application/vnd.oci.image.layer.v1.tar+gzip"){const i=e.replace(/^sha256:/,""),r=path.join(this.blobsDir,i);let a=0;Buffer.isBuffer(t)?(fs.writeFileSync(r,t),a=t.length):"string"==typeof t&&fs.existsSync(t)&&(t!==r&&fs.copyFileSync(t,r),a=fs.statSync(r).size);try{this.db.prepare("\n INSERT OR REPLACE INTO blobs (digest, size_bytes, media_type, created_at)\n VALUES (?, ?, ?, ?)\n ").run(i,a,s,(new Date).toISOString())}catch(e){}return{digest:i,path:r,sizeBytes:a}}registerImage({repo:e,tag:t="latest",digest:s,platform:i,sizeBytes:r=0,layersCount:a=0,rootfsDir:o,config:n={},blobDigests:l=[]}){const c="img-"+crypto.createHash("sha256").update(`${e}:${t}:${s||Date.now()}`).digest("hex").slice(0,12),d=(new Date).toISOString(),b="string"==typeof n?n:JSON.stringify(n);try{this.db.prepare("\n INSERT OR REPLACE INTO images (\n id, repo, tag, digest, platform, size_bytes, layers_count, pulled_at, last_used_at, rootfs_dir, config_json\n ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)\n ").run(c,e,t,s,i,r,a,d,d,o,b);const n=this.db.prepare("\n INSERT OR IGNORE INTO image_blobs (image_id, blob_digest)\n VALUES (?, ?)\n ");for(const e of l){const t=e.replace(/^sha256:/,"");n.run(c,t)}}catch(e){}return{id:c,repo:e,tag:t,digest:s,rootfsDir:o}}listImages(){try{return this.db.prepare("\n SELECT id, repo, tag, digest, platform, size_bytes, layers_count, pulled_at, last_used_at, rootfs_dir\n FROM images\n ORDER BY pulled_at DESC\n ").all().map(e=>({id:e.id,repo:e.repo,tag:e.tag,fullRef:`${e.repo}:${e.tag}`,digest:e.digest,platform:e.platform,sizeBytes:e.size_bytes,sizeMB:(e.size_bytes/1048576).toFixed(1),layersCount:e.layers_count,pulledAt:e.pulled_at,lastUsedAt:e.last_used_at,rootfsDir:e.rootfs_dir}))}catch(e){return[]}}getImage(e){if(!e)return null;let t,s;if(e.includes(":")){const i=e.split(":");t=i[0],s=i[1]}else t=e,s="latest";try{let i=this.db.prepare("\n SELECT * FROM images WHERE repo = ? AND tag = ?\n ").all(t,s);if(i.length||e.includes(":")||(i=this.db.prepare("\n SELECT * FROM images WHERE id LIKE ?\n ").all(`${e}%`)),i&&i.length>0){const e=i[0];let t={};try{t=JSON.parse(e.config_json)}catch(e){}return{id:e.id,repo:e.repo,tag:e.tag,fullRef:`${e.repo}:${e.tag}`,digest:e.digest,platform:e.platform,sizeBytes:e.size_bytes,sizeMB:(e.size_bytes/1048576).toFixed(1),layersCount:e.layers_count,pulledAt:e.pulled_at,lastUsedAt:e.last_used_at,rootfsDir:e.rootfs_dir,config:t}}}catch(e){}return null}tagImage(e,t){const s=this.getImage(e);if(!s)throw new Error(`Source image "${e}" not found.`);let i,r;if(t.includes(":")){const e=t.split(":");i=e[0],r=e[1]}else i=t,r="latest";return this.registerImage({repo:i,tag:r,digest:s.digest,platform:s.platform,sizeBytes:s.sizeBytes,layersCount:s.layersCount,rootfsDir:s.rootfsDir,config:s.config})}removeImage(e){const t=this.getImage(e);if(!t)throw new Error(`Image "${e}" not found locally.`);try{this.db.prepare("DELETE FROM images WHERE id = ?").run(t.id)}catch(e){}if(0===this.listImages().filter(e=>e.rootfsDir===t.rootfsDir).length&&t.rootfsDir&&fs.existsSync(t.rootfsDir))try{fs.removeSync(t.rootfsDir)}catch(e){}return{success:!0,removed:t.fullRef}}pruneBlobs(){let e=0,t=0;try{const s=this.db.prepare("SELECT DISTINCT blob_digest FROM image_blobs").all(),i=new Set(s.map(e=>e.blob_digest));if(fs.existsSync(this.blobsDir)){const s=fs.readdirSync(this.blobsDir);for(const r of s)if(!i.has(r)){const s=path.join(this.blobsDir,r);try{e+=fs.statSync(s).size,fs.unlinkSync(s),t++}catch(e){}}}const r=this.db.prepare("SELECT digest FROM blobs").all(),a=this.db.prepare("DELETE FROM blobs WHERE digest = ?");for(const e of r)i.has(e.digest)||a.run(e.digest)}catch(e){}return{reclaimedBytes:e,reclaimedMB:(e/1048576).toFixed(1),prunedCount:t}}}module.exports=ImageStore;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const SandboxEngine=require("./engine"),SandboxNetwork=require("./network"),SandboxStorage=require("./storage"),MacOSSandbox=require("./macos-sandbox"),CgroupsV2=require("./cgroups"),WindowsWSL2=require("./windows-wsl2"),CapsuleBuilder=require("./capsule-builder"),RuntimeManager=require("./runtime-manager");module.exports={SandboxEngine,SandboxNetwork,SandboxStorage,MacOSSandbox,CgroupsV2,WindowsWSL2,CapsuleBuilder,RuntimeManager};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const{execSync,spawn}=require("child_process");let fs;try{fs=require("fs-extra")}catch(e){try{fs=require("/Users/ramansingh/ThinkNCollab-Shell-V2/node_modules/fs-extra")}catch(e){const n=require("fs");fs={...n,ensureDirSync:e=>n.mkdirSync(e,{recursive:!0}),readJsonSync:e=>JSON.parse(n.readFileSync(e,"utf8")),writeJsonSync:(e,r)=>n.writeFileSync(e,JSON.stringify(r,null,2),"utf8"),removeSync:e=>n.rmSync(e,{recursive:!0,force:!0})}}}const path=require("path"),os=require("os"),PROFILES_DIR=path.join(os.homedir(),".tnc","profiles");class MacOSSandbox{constructor(){this.isMac="darwin"===process.platform,fs.ensureDirSync(PROFILES_DIR)}generateSeatbeltProfile(e,n={}){const{allowNetwork:r=!0,allowRead:t=!0,workspacePath:o=process.cwd(),allowedPorts:s=[]}=n,l=`\n(version 1)\n\n;; --- Default: deny everything ---\n(deny default)\n\n;; --- Process basics ---\n(allow process-exec)\n(allow process-fork)\n(allow signal)\n(allow mach-lookup)\n(allow sysctl-read)\n(allow system-socket)\n\n;; --- Allow reading system libraries & paths ---\n(allow file-read-metadata)\n(allow file-read*\n (subpath "/usr")\n (subpath "/lib")\n (subpath "/System")\n (subpath "/private")\n (subpath "/dev")\n (literal "/")\n (literal "/etc")\n (literal "/tmp")\n (literal "/var")\n (literal "/Users")\n (subpath "${os.homedir()}")\n)\n\n;; --- Allow Node.js / nvm / homebrew ---\n(allow file-read*\n (subpath "${os.homedir()}/.nvm")\n (subpath "${os.homedir()}/.node")\n (subpath "${os.homedir()}/.npm")\n (subpath "/opt/homebrew")\n (subpath "/usr/local")\n)\n\n;; --- Allow workspace directory (read+write) ---\n${t?`\n(allow file-read* (subpath "${o}"))\n(allow file-write* (subpath "${o}"))\n`:""}\n\n;; --- Allow TNC state directory ---\n(allow file-read* (subpath "${os.homedir()}/.tnc"))\n(allow file-write* (subpath "${os.homedir()}/.tnc"))\n\n;; --- Temp files ---\n(allow file-read* (subpath "/tmp"))\n(allow file-write* (subpath "/tmp"))\n(allow file-read* (subpath "/var/folders"))\n(allow file-write* (subpath "/var/folders"))\n\n;; --- Allow stdout/stderr ---\n(allow file-write-data\n (literal "/dev/stdout")\n (literal "/dev/stderr")\n (literal "/dev/null")\n)\n\n;; --- Networking ---\n${r?"\n(allow network-outbound)\n(allow network-inbound)\n(allow network-bind)\n":"\n;; Network blocked — no external access\n(deny network-outbound)\n(deny network-inbound)\n"}\n\n;; --- IPC basics ---\n(allow ipc-posix-shm)\n(allow ipc-posix-sem)\n`,a=path.join(PROFILES_DIR,`${e}.sb`);return fs.writeFileSync(a,l.trim()),a}buildResourceLimitScript(e,n=2048){return{prelude:`ulimit -v ${1024*n} 2>/dev/null || true; ulimit -n 4096 2>/dev/null || true`,cmd:e}}setupPortForward(e,n){if(!this.isMac)return!1;try{const r=`tnc_${e}_${n}`,t=`rdr pass on lo0 proto tcp from any to any port ${e} -> 127.0.0.1 port ${n}\n`,o=`/tmp/tnc_pf_${r}.conf`;return fs.writeFileSync(o,t),execSync(`sudo -n pfctl -a "tnc/${r}" -f ${o} 2>/dev/null || true`,{stdio:"ignore"}),execSync("sudo -n pfctl -e 2>/dev/null || true",{stdio:"ignore"}),{success:!0,rule:t.trim()}}catch(e){return{success:!1,reason:e.message}}}removePortForward(e,n){if(!this.isMac)return!1;try{return execSync(`sudo -n pfctl -a "tnc/tnc_${e}_${n}" -F all 2>/dev/null || true`,{stdio:"ignore"}),!0}catch{return!1}}spawnSandboxed(e,n,r={}){const{cwd:t=process.cwd(),hostPort:o=null,containerPort:s=null,memoryLimit:l="2g",allowNetwork:a=!0,outFd:i=null,runtimeBinDir:c=null}=r;if(!this.isMac)throw new Error("MacOSSandbox only runs on macOS/Darwin");let u=2048;if(l){const e=String(l).match(/^(\d+)([gGmM]?)$/);e&&(u=parseInt(e[1])*("g"===e[2].toLowerCase()?1024:1))}const d=this.generateSeatbeltProfile(e,{allowNetwork:a,workspacePath:t}),{prelude:p}=this.buildResourceLimitScript(n,u);o&&s&&o!==s&&this.setupPortForward(o,s);const w=path.join(os.homedir(),".tnc","sandboxes",e);fs.ensureDirSync(w);const h=path.join(w,"entrypoint.sh"),f=path.join(w,"output.log"),m=`#!/bin/bash\n${c?`export PATH=${JSON.stringify(c)}:"$PATH"\n`:""}${p}\ncd ${JSON.stringify(t)} 2>/dev/null || true\nexec /bin/bash -c ${JSON.stringify(n)} >> ${JSON.stringify(f)} 2>&1\n`;fs.writeFileSync(h,m,{mode:493});const b=spawn("sandbox-exec",["-f",d,"/bin/bash",h],{cwd:t,detached:!0,stdio:["ignore","ignore","ignore"],env:{...process.env,TNC_SANDBOX_ID:e,TNC_PLATFORM:"macos-seatbelt",PORT:String(o||s||3e3),HOME:os.homedir(),PATH:c?`${c}:${process.env.PATH}`:process.env.PATH}});return b.unref(),{child:b,profilePath:d,isolationLevel:"macos-seatbelt",memoryLimitMB:u}}getMemoryUsage(e){if(!e)return 0;try{const n=execSync(`ps -o rss= -p ${e} 2>/dev/null || echo 0`,{encoding:"utf8"}).trim();return Math.round(parseInt(n||"0")/1024)}catch{return 0}}cleanupProfile(e){const n=path.join(PROFILES_DIR,`${e}.sb`);try{fs.removeSync(n)}catch(e){}}}module.exports=MacOSSandbox;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ "use strict";const path=require("path"),os=require("os"),net=require("net"),fs=require("fs-extra"),{spawn,spawnSync,execSync}=require("child_process"),chalk=require("chalk"),TNC_DIR=path.join(os.homedir(),".tnc"),TNC_BIN_DIR=path.join(TNC_DIR,"bin"),TNC_VZ_BIN=path.join(TNC_BIN_DIR,"tnc-vz"),VM_PID_FILE=path.join(TNC_DIR,"microvm.pid"),VM_SOCK=path.join(TNC_DIR,"vmctl.sock"),KERNEL_DIR=path.join(TNC_DIR,"kernel"),SANDBOX_DIR=path.join(TNC_DIR,"sandboxes"),SETUP_SCRIPT=path.join(__dirname,"..","..","..","native","tnc-vz","scripts","setup-microvm.sh");function sendCommand(n,e=15e3){return new Promise((t,s)=>{const r=net.createConnection(VM_SOCK,()=>{const e=JSON.stringify(n)+"\n";r.write(e)});let i="";const o=setTimeout(()=>{r.destroy(),s(new Error(`MicroVM command timed out: ${n.cmd}`))},e);r.on("data",n=>{i+=n.toString();const e=i.indexOf("\n");if(-1!==e){clearTimeout(o),r.destroy();try{t(JSON.parse(i.slice(0,e)))}catch(n){s(new Error(`Bad response from MicroVM: ${i.slice(0,e)}`))}}}),r.on("error",n=>{clearTimeout(o),s(new Error(`vsock relay error: ${n.message}`))})})}class MicroVMManager{constructor(){fs.ensureDirSync(TNC_DIR),fs.ensureDirSync(SANDBOX_DIR),this._startingVM=!1}isTncVzAvailable(){try{return fs.existsSync(TNC_VZ_BIN)&&fs.statSync(TNC_VZ_BIN).size>1e4}catch(n){return!1}}isKernelReady(){const n=path.join(KERNEL_DIR,"vmlinuz"),e=path.join(KERNEL_DIR,"initramfs.img");return fs.existsSync(n)&&fs.existsSync(e)}async runSetup(){if(!fs.existsSync(SETUP_SCRIPT))throw new Error(`MicroVM setup script not found: ${SETUP_SCRIPT}\nPlease reinstall ThinkNCollab Shell: npm install -g thinkncollab-shell`);return console.log(chalk.cyan("\n[TNC MicroVM] First-time setup — downloading Alpine Linux kernel (~15MB)...")),console.log(chalk.dim("This runs once. Subsequent boots take 0.3 seconds.\n")),new Promise((n,e)=>{spawn("bash",[SETUP_SCRIPT],{stdio:"inherit"}).on("close",t=>{0===t?n():e(new Error(`MicroVM setup failed (exit ${t})`))})})}isVMRunning(){try{const n=fs.readFileSync(VM_PID_FILE,"utf8").trim(),e=parseInt(n,10);return!!e&&(process.kill(e,0),!0)}catch(n){return!1}}async pingVM(){try{const n=await sendCommand({cmd:"ping"},2e3);return n&&n.ok&&n.pong}catch(n){return!1}}async ensureVMRunning(n={}){if(this._startingVM)return await new Promise(n=>setTimeout(n,500)),this.ensureVMRunning(n);if(this.isTncVzAvailable()&&this.isKernelReady()||await this.runSetup(),this.isVMRunning()){if(await this.pingVM())return;this._killOrphanVM()}this._startingVM=!0;try{console.log(chalk.cyan("[TNC MicroVM] Starting Linux MicroVM..."));const e=["start","--cpus",String(n.cpus||2),"--mem",String(n.memMB||512),"--workspace",n.workspace||os.homedir()];spawn(TNC_VZ_BIN,e,{detached:!0,stdio:"ignore"}).unref(),console.log(chalk.dim(" Waiting for MicroVM guest-init..."));const t=Date.now()+1e4;for(;Date.now()<t;)if(await new Promise(n=>setTimeout(n,300)),fs.existsSync(VM_SOCK)&&await this.pingVM()){const n=1e4-(t-Date.now());return void console.log(chalk.green(`[TNC MicroVM] Ready in ${n}ms ✓`))}throw new Error("MicroVM did not become ready within 10 seconds")}finally{this._startingVM=!1}}_killOrphanVM(){try{const n=fs.readFileSync(VM_PID_FILE,"utf8").trim(),e=parseInt(n,10);e&&process.kill(e,"SIGTERM")}catch(n){}try{fs.removeSync(VM_PID_FILE)}catch(n){}try{fs.removeSync(VM_SOCK)}catch(n){}}async stopVM(){if(this.isTncVzAvailable()){spawnSync(TNC_VZ_BIN,["stop"],{stdio:"pipe"});try{fs.removeSync(VM_PID_FILE)}catch(n){}try{fs.removeSync(VM_SOCK)}catch(n){}console.log(chalk.green("[TNC MicroVM] Stopped."))}}async getStatus(){const n=this.isVMRunning(),e=!!n&&await this.pingVM();return{available:this.isTncVzAvailable(),kernelReady:this.isKernelReady(),running:n,responsive:e}}async start(n,e={}){await this.ensureVMRunning();let t=null,s=e.cmd||null,r=e.args||null,i="/",o=[];if(e.image)try{const n=new(require("./oci-puller"));let a=n.inspect(e.image);if(a||(console.log(chalk.dim(` Pulling image: ${e.image}...`)),a=(await n.pull(e.image)).meta),a){if(a.rootfsDir&&fs.existsSync(a.rootfsDir)&&(t=a.rootfsDir),a.env&&Array.isArray(a.env)&&(o=a.env),!r&&!e.cmd){const n=a.entrypoint||[],e=a.cmd||[];(n.length||e.length)&&(r=[...n,...e],s=r.join(" "))}a.workingDir&&""!==a.workingDir&&"/workspace"!==a.workingDir&&(i=a.workingDir)}}catch(n){console.warn(chalk.yellow(`[MicroVM] Image resolution: ${n.message}`))}t||(t=e.cwd||process.cwd()),s||r||(s="/bin/sh");const a={};for(const n of o)if("string"==typeof n){const e=n.indexOf("=");-1!==e&&(a[n.slice(0,e)]=n.slice(e+1))}if(e.env)if(Array.isArray(e.env)){for(const n of e.env)if("string"==typeof n){const e=n.indexOf("=");-1!==e&&(a[n.slice(0,e)]=n.slice(e+1))}}else if("object"==typeof e.env&&null!==e.env)for(const[n,t]of Object.entries(e.env))a[n]=String(t);const c=Object.entries(a).map(([n,e])=>`${n}=${e}`);let l=e.workdir||i||"/";"/workspace"===l&&(l="/");const m=[];if(Array.isArray(e.volumes))for(const n of e.volumes){let e=null,t=null,s="rw";if("string"==typeof n){const r=n.split(":");r.length>=2&&(e=r[0],t=r[1],!r[2]||"ro"!==r[2]&&"rw"!==r[2]||(s=r[2]))}else"object"==typeof n&&null!==n&&(e=n.host||n.source||n.src,t=n.container||n.destination||n.target||n.dst,s=n.mode||(n.readOnly?"ro":"rw"));if(e&&t){e.startsWith("~")&&(e=path.join(os.homedir(),e.slice(1)));const n=path.resolve(process.cwd(),e);try{fs.ensureDirSync(n)}catch(n){}m.push({host:n,container:t.startsWith("/")?t:`/${t}`,mode:"ro"===s?"ro":"rw"})}}const d=await sendCommand({cmd:"run",id:n,name:e.name||n,rootfs:t,exec:s,args:r,workdir:l,env:c,volumes:m},3e4);if(!d.ok)throw new Error(`MicroVM container start failed: ${d.error}`);let p=d.vm_ip&&!d.vm_ip.startsWith("127.")?d.vm_ip:null;if(!p)try{const n=await sendCommand({cmd:"ping"},2e3);n&&n.vm_ip&&!n.vm_ip.startsWith("127.")&&(p=n.vm_ip)}catch(n){}p||(p="192.168.64.2");const u={id:n,name:e.name||n,image:e.image||null,state:"RUNNING",pid:d.pid,cmd:s||(r?r.join(" "):"/bin/sh"),cwd:t,vmIp:p,hostPort:e.hostPort||null,containerPort:e.containerPort||null,volumes:m,environment:a,isolationLevel:"macos-microvm-vz",platform:"linux/arm64",createdAt:(new Date).toISOString(),startedAt:(new Date).toISOString()};return this._saveMeta(n,u),e.hostPort&&this._startPortForward(n,e.hostPort,e.containerPort||e.hostPort,p),console.log(chalk.green(`[TNC MicroVM] Container "${n}" started (PID ${d.pid} in Linux VM)`)),{success:!0,meta:u}}_startPortForward(n,e,t,s="192.168.64.2"){const r=path.join(SANDBOX_DIR,n);fs.ensureDirSync(r);const i=path.join(r,"proxy.pid");if(fs.existsSync(i)){try{const n=parseInt(fs.readFileSync(i,"utf8").trim(),10);n&&process.kill(n,"SIGTERM")}catch(n){}try{fs.removeSync(i)}catch(n){}}try{const{execSync:n}=require("child_process"),s=n(`lsof -i :${e} -sTCP:LISTEN -Fpc 2>/dev/null`,{encoding:"utf8"}).trim();if(s){const r=s.split("\n");let i=null,o="unknown";for(const n of r)n.startsWith("p")&&(i=parseInt(n.slice(1),10)),n.startsWith("c")&&(o=n.slice(1));if(i){let s=!1;try{n(`ps -p ${i} -o args= 2>/dev/null`,{encoding:"utf8"}).includes("port-proxy-daemon.js")&&i!==process.pid&&(s=!0)}catch(n){}if(!s)throw new Error(`Host port ${e} is already in use by process PID ${i} (${o}). Please stop the conflicting service or choose a different port (e.g. -p <otherPort>:${t}).`);try{process.kill(i,"SIGTERM")}catch(n){}}}}catch(n){if(n.message&&n.message.includes("Host port"))throw n}const o=path.join(__dirname,"port-proxy-daemon.js"),a=spawn(process.execPath,[o,n,String(e),String(t),s],{detached:!0,stdio:"ignore"});a.unref(),console.log(chalk.cyan(` [Port Mapping] 0.0.0.0:${e} -> ${s}:${t} (PID ${a.pid})`))}async stop(n){await this.ensureVMRunning();const e=path.join(SANDBOX_DIR,n,"proxy.pid");if(fs.existsSync(e)){try{const n=parseInt(fs.readFileSync(e,"utf8").trim(),10);n&&process.kill(n,"SIGTERM")}catch(n){}try{fs.removeSync(e)}catch(n){}}const t=await sendCommand({cmd:"stop",id:n},15e3),s=this._loadMeta(n);return s&&(s.state="STOPPED",this._saveMeta(n,s)),{success:t.ok,message:`Container "${n}" stopped.`}}async list(){if(!this.isVMRunning())return[];try{const n=await sendCommand({cmd:"ps"},5e3);return(n.ok?n.containers:[]).map(n=>({...this._loadMeta(n.id)||{},id:n.id,pid:n.pid,state:n.running?"RUNNING":"STOPPED",vmIp:n.ip,isolationLevel:"macos-microvm-vz"}))}catch(n){return[]}}async exec(n,e){return await this.ensureVMRunning(),sendCommand({cmd:"exec",id:n,exec:e},3e4)}async commit(n){return await this.ensureVMRunning(),sendCommand({cmd:"commit",id:n},6e4)}async logs(n,e=100){return await this.ensureVMRunning(),sendCommand({cmd:"logs",id:n,tail:e},1e4)}async stats(n){return await this.ensureVMRunning(),sendCommand({cmd:"stats",id:n},5e3)}async top(n){return await this.ensureVMRunning(),sendCommand({cmd:"top",id:n},1e4)}_metaPath(n){return path.join(SANDBOX_DIR,n,"meta.json")}_saveMeta(n,e){const t=path.join(SANDBOX_DIR,n);fs.ensureDirSync(t),fs.writeJsonSync(this._metaPath(n),e,{spaces:2})}_loadMeta(n){try{return fs.readJsonSync(this._metaPath(n))}catch(n){return null}}}let _instance=null;function getMicroVMManager(){return _instance||(_instance=new MicroVMManager),_instance}module.exports={MicroVMManager,getMicroVMManager,sendCommand};
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),os=require("os"),fs=require("fs-extra"),{spawnSync}=require("child_process"),chalk=require("chalk"),DATA_DIR=path.join(os.homedir(),".tnc","data"),SANDBOX_DIR=path.join(os.homedir(),".tnc","sandboxes");class NativeServiceManager{constructor(t=null){if(t)this.engine=t;else{const{SandboxEngine:t}=require("./index");this.engine=new t}fs.ensureDirSync(DATA_DIR),fs.ensureDirSync(SANDBOX_DIR)}_findBinary(t){try{const s=spawnSync("which",[t],{stdio:"pipe"});if(0===s.status)return s.stdout.toString().trim()}catch(t){}const s=path.join("/opt/homebrew/bin",t);if(fs.existsSync(s))return s;const e=path.join("/usr/local/bin",t);if(fs.existsSync(e))return e;const o=path.join("/usr/bin",t);return fs.existsSync(o)?o:null}async startPostgres(t={}){const s=t.port||5432,e=path.join(DATA_DIR,"postgres"),o=path.join(SANDBOX_DIR,"postgres"),n=path.join(o,"output.log");fs.ensureDirSync(o);const i=this._findBinary("pg_ctl"),r=this._findBinary("initdb");if(this._findBinary("postgres"),!i||!r){if(t.autoInstall)return console.log(chalk.yellow("\n[Notice] PostgreSQL not found on host. Auto-installing native PostgreSQL via system package manager...")),this.installPostgres(),this.startPostgres({...t,autoInstall:!1});throw new Error("PostgreSQL binaries not found on host.\nInstall natively without Docker:\n Run: thinknsh service install postgres\n Or: brew install postgresql@16 (macOS) / sudo apt install postgresql (Linux)")}if(!fs.existsSync(path.join(e,"PG_VERSION"))){if(console.log(chalk.cyan(`\n[Native:Postgres] Initializing local database cluster in ${e}...`)),fs.ensureDirSync(e),0!==spawnSync(r,["-D",e,"-U","postgres","-A","trust","-E","UTF-8","--no-locale"],{stdio:"inherit"}).status)throw new Error("Failed to initialize PostgreSQL database cluster.");console.log(chalk.green("[OK] PostgreSQL cluster initialized cleanly!"))}try{const t=spawnSync("lsof",["-i",`:${s}`],{stdio:"pipe"});if(0===t.status&&t.stdout.toString().includes("postgres"))return console.log(chalk.green(`\n[OK] Native PostgreSQL is ALREADY ACTIVE on localhost:${s}!`)),console.log(chalk.dim(` Host Endpoint: postgresql://localhost:${s}/postgres`)),console.log(chalk.dim(" RAM Overhead: Native host process (~25MB, 0 MB VM)")),console.log(chalk.dim(" Docker Hub: ZERO bytes downloaded ✓\n")),this._updateServiceMeta("postgres",{state:"RUNNING",port:s,dataDir:e,logFile:n})}catch(t){}if(0===spawnSync(i,["-D",e,"status"],{stdio:"pipe"}).status)return console.log(chalk.green(`[OK] PostgreSQL is already running on port ${s}.`)),this._updateServiceMeta("postgres",{state:"RUNNING",port:s,dataDir:e,logFile:n});if(console.log(chalk.cyan(`[Native:Postgres] Starting native PostgreSQL daemon on port ${s} (0 MB VM)...`)),0!==spawnSync(i,["-D",e,"-l",n,"start","-o",`-p ${s}`],{stdio:"inherit"}).status)throw new Error(`Failed to start native PostgreSQL daemon. Check logs: ${n}`);const a=this._updateServiceMeta("postgres",{state:"RUNNING",port:s,dataDir:e,logFile:n});return console.log(chalk.green(`\n[OK] Native PostgreSQL is ACTIVE on port ${s}!`)),console.log(chalk.dim(` Host Endpoint: postgresql://postgres@localhost:${s}/postgres`)),console.log(chalk.dim(` Data Directory: ${e}`)),console.log(chalk.dim(" RAM Overhead: Native process (~25MB, 0 MB VM)")),console.log(chalk.dim(" Docker Hub: ZERO bytes downloaded ✓\n")),a}stopPostgres(){const t=path.join(DATA_DIR,"postgres"),s=this._findBinary("pg_ctl");if(!s)throw new Error("pg_ctl binary not found.");return console.log(chalk.yellow("Stopping native PostgreSQL daemon...")),spawnSync(s,["-D",t,"stop","-m","fast"],{stdio:"inherit"}),this._updateServiceMeta("postgres",{state:"STOPPED"})}statusPostgres(){const t=path.join(DATA_DIR,"postgres"),s=this._findBinary("pg_ctl");let e=!1,o="";try{const t=spawnSync("lsof",["-i",":5432"],{stdio:"pipe"});0===t.status&&t.stdout.toString().includes("postgres")&&(e=!0,o="Active on localhost:5432 (Native host process)")}catch(t){}if(!e&&s){const n=spawnSync(s,["-D",t,"status"],{stdio:"pipe"});0===n.status&&(e=!0,o=n.stdout.toString().trim())}return{service:"postgres",running:e,port:e?5432:null,dataDir:t,details:o||"Stopped"}}async startRedis(t={}){const s=t.port||6379,e=path.join(DATA_DIR,"redis"),o=path.join(SANDBOX_DIR,"redis"),n=path.join(o,"output.log");fs.ensureDirSync(e),fs.ensureDirSync(o);const i=this._findBinary("redis-server");if(!i)throw new Error("redis-server not found on host.\nInstall natively without Docker:\n macOS: brew install redis\n Linux: sudo apt install redis-server");console.log(chalk.cyan(`[Native:Redis] Starting native Redis on port ${s}...`));const{spawn:r}=require("child_process"),a=fs.openSync(n,"a");r(i,["--port",String(s),"--dir",e,"--daemonize","yes"],{detached:!0,stdio:["ignore",a,a]}).unref();const l=this._updateServiceMeta("redis",{state:"RUNNING",port:s,dataDir:e,logFile:n});return console.log(chalk.green(`[OK] Native Redis is ACTIVE on port ${s}!`)),l}stopRedis(){const t=this._findBinary("redis-cli");return t&&spawnSync(t,["shutdown"],{stdio:"pipe"}),this._updateServiceMeta("redis",{state:"STOPPED"})}installPostgres(){console.log(chalk.cyan("\n[Native:Install] Provisioning native PostgreSQL without Docker..."));const t="darwin"===process.platform,s="linux"===process.platform,e="win32"===process.platform;if(t){const t=this._findBinary("brew");if(!t)throw new Error("Homebrew not found. Please install Homebrew or install PostgreSQL manually.");if(console.log(chalk.dim(" Executing: brew install postgresql@16")),0!==spawnSync(t,["install","postgresql@16"],{stdio:"inherit"}).status)throw new Error("Failed to install PostgreSQL via Homebrew.");try{spawnSync(t,["link","postgresql@16","--force"],{stdio:"pipe"})}catch(t){}return console.log(chalk.green("✓ Native PostgreSQL 16 installed successfully!")),!0}if(s&&this._findBinary("apt-get")){if(console.log(chalk.dim(" Executing: sudo apt-get update && sudo apt-get install -y postgresql")),0!==spawnSync("sudo",["apt-get","install","-y","postgresql"],{stdio:"inherit"}).status)throw new Error("Failed to install PostgreSQL via apt.");return console.log(chalk.green("✓ Native PostgreSQL installed successfully!")),!0}if(e&&this._findBinary("winget"))return console.log(chalk.dim(" Executing: winget install PostgreSQL.PostgreSQL.16")),spawnSync("winget",["install","PostgreSQL.PostgreSQL.16","--silent"],{stdio:"inherit"}),!0;throw new Error("Unsupported platform package manager. Please install PostgreSQL manually.")}installRedis(){console.log(chalk.cyan("\n[Native:Install] Provisioning native Redis without Docker..."));const t="darwin"===process.platform,s="linux"===process.platform;if(t){const t=this._findBinary("brew");if(!t)throw new Error("Homebrew not found.");if(console.log(chalk.dim(" Executing: brew install redis")),0!==spawnSync(t,["install","redis"],{stdio:"inherit"}).status)throw new Error("Failed to install Redis via Homebrew.");return console.log(chalk.green("✓ Native Redis installed successfully!")),!0}if(s&&this._findBinary("apt-get")){if(0!==spawnSync("sudo",["apt-get","install","-y","redis-server"],{stdio:"inherit"}).status)throw new Error("Failed to install Redis via apt.");return console.log(chalk.green("✓ Native Redis installed successfully!")),!0}throw new Error("Unsupported platform package manager for Redis.")}_updateServiceMeta(t,s={}){const e=path.join(SANDBOX_DIR,t,"meta.json");fs.ensureDirSync(path.dirname(e));let o={};if(fs.existsSync(e))try{o=fs.readJsonSync(e)}catch(t){}return o={id:t,name:t,cmd:`native ${t}`,cwd:s.dataDir||o.cwd||process.cwd(),hostPort:s.port||o.hostPort,containerPort:s.port||o.containerPort,state:s.state||"RUNNING",isolationLevel:"native-host-daemon",isNative:!0,updatedAt:(new Date).toISOString()},fs.writeJsonSync(e,o,{spaces:2}),o}}module.exports=NativeServiceManager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const{execSync}=require("child_process");class SandboxNetwork{constructor(e={}){this.bridge=e.bridgeName||"br-tnc0",this.subnet=e.subnet||"10.89.0.0/24",this.gatewayIp=e.gatewayIp||"10.89.0.1",this.isLinux="linux"===process.platform}setupBridge(){if(!this.isLinux)return{status:"unsupported_platform",platform:process.platform};try{return execSync(`ip link show ${this.bridge}`,{stdio:"ignore"}),{status:"exists",bridge:this.bridge}}catch{try{return execSync(`ip link add name ${this.bridge} type bridge`),execSync(`ip addr add ${this.gatewayIp}/24 dev ${this.bridge}`),execSync(`ip link set dev ${this.bridge} up`),execSync("sysctl -w net.ipv4.ip_forward=1",{stdio:"ignore"}),execSync(`iptables -t nat -A POSTROUTING -s ${this.subnet} ! -o ${this.bridge} -j MASQUERADE`,{stdio:"ignore"}),{status:"created",bridge:this.bridge}}catch(e){return{status:"error",message:e.message}}}}attachNetwork(e,t,i="10.89.0.2"){if(!this.isLinux)return{status:"skipped",reason:"non-linux"};const s=e.replace(/[^a-zA-Z0-9]/g,"").slice(0,6),r=`vh_${s}`,n=`vg_${s}`;try{execSync(`ip link add ${r} type veth peer name ${n}`),execSync(`ip link set ${r} master ${this.bridge}`),execSync(`ip link set ${r} up`),execSync(`ip link set ${n} netns ${t}`);const e=`nsenter -t ${t} -n`;return execSync(`${e} ip link set dev ${n} name eth0`),execSync(`${e} ip addr add ${i}/24 dev eth0`),execSync(`${e} ip link set dev lo up`),execSync(`${e} ip link set dev eth0 up`),execSync(`${e} ip route add default via ${this.gatewayIp}`),{status:"attached",vethHost:r,guestIp:i,gateway:this.gatewayIp}}catch(e){return{status:"error",message:e.message}}}mapPort(e,t,i){if(!this.isLinux||!e||!i)return!1;try{return execSync(`iptables -t nat -A PREROUTING -p tcp --dport ${e} -j DNAT --to-destination ${t}:${i}`,{stdio:"ignore"}),!0}catch{return!1}}unmapPort(e,t,i){if(!this.isLinux||!e||!i)return!1;try{return execSync(`iptables -t nat -D PREROUTING -p tcp --dport ${e} -j DNAT --to-destination ${t}:${i}`,{stdio:"ignore"}),!0}catch{return!1}}}module.exports=SandboxNetwork;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const fs=require("fs-extra"),path=require("path"),os=require("os"),crypto=require("crypto"),{execSync,spawnSync}=require("child_process"),https=require("https"),http=require("http"),chalk=require("chalk");class OCIExporter{constructor(){this.imagesBase=path.join(os.homedir(),".tnc","images")}parsePushTarget(e){return e.startsWith("ecr://")||e.startsWith("aws://")?{type:"ecr",ref:e.replace(/^(ecr|aws):\/\//,"")}:e.startsWith("gcr://")||e.startsWith("gcp://")||e.startsWith("gar://")?{type:"gcr",ref:e.replace(/^(gcr|gcp|gar):\/\//,"")}:e.startsWith("ghcr://")||e.startsWith("ghcr.io/")?{type:"ghcr",ref:e.replace(/^ghcr:\/\//,"ghcr.io/")}:e.startsWith("docker://")||e.includes("docker.io/")?{type:"dockerhub",ref:e.replace(/^docker:\/\//,"")}:{type:"generic",ref:e}}async exportTar(e,t){const s=this._loadManifest(),o=s[e]||s[`${e}:latest`];if(!o)throw new Error(`Image "${e}" not found locally. Run: thinknsh pull ${e}`);console.log(chalk.cyan(`\n[OCI] Exporting "${e}" as OCI tar...`));const r=path.join(os.tmpdir(),`thinknsh-oci-export-${Date.now()}`);fs.ensureDirSync(r);try{fs.writeJsonSync(path.join(r,"oci-layout"),{imageLayoutVersion:"1.0.0"});const s=path.join(r,"blobs","sha256");fs.ensureDirSync(s);const n={architecture:o.platform?.split("/")[1]||"amd64",os:o.platform?.split("/")[0]||"linux",rootfs:{type:"layers",diff_ids:[]},config:{Entrypoint:o.entrypoint||[],Cmd:o.cmd||["sh"],Env:o.env||[],WorkingDir:o.workingDir||"/",Labels:o.labels||{},ExposedPorts:o.exposedPorts||{}},history:[{created:o.pulledAt||(new Date).toISOString(),created_by:`thinknsh export ${e}`}]},i=path.join(r,"layer.tar"),a=o.rootfsDir;a&&fs.existsSync(a)?(process.stdout.write(chalk.dim(" Packing rootfs... ")),spawnSync("tar",["-cf",i,"-C",a,"."],{stdio:"pipe"}),console.log(chalk.green("✓"))):spawnSync("tar",["-cf",i,"--files-from","/dev/null"],{stdio:"pipe"});const c=fs.readFileSync(i),p=crypto.createHash("sha256").update(c).digest("hex");n.rootfs.diff_ids.push(`sha256:${p}`);const h=`${i}.gz`;process.stdout.write(chalk.dim(" Compressing layer... ")),spawnSync("gzip",["-f",i],{stdio:"pipe"}),console.log(chalk.green("✓"));const l=fs.readFileSync(h),g=crypto.createHash("sha256").update(l).digest("hex"),d=l.length;fs.copyFileSync(h,path.join(s,g)),fs.removeSync(h);const u=Buffer.from(JSON.stringify(n)),y=crypto.createHash("sha256").update(u).digest("hex");fs.writeFileSync(path.join(s,y),u);const f={schemaVersion:2,mediaType:"application/vnd.oci.image.manifest.v1+json",config:{mediaType:"application/vnd.oci.image.config.v1+json",digest:`sha256:${y}`,size:u.length},layers:[{mediaType:"application/vnd.oci.image.layer.v1.tar+gzip",digest:`sha256:${g}`,size:d}],annotations:{"org.opencontainers.image.title":e,"org.opencontainers.image.created":(new Date).toISOString(),"org.opencontainers.image.vendor":"ThinkNCollab","io.thinkncollab.shell.version":"2.0.0"}},m=Buffer.from(JSON.stringify(f,null,2)),k=crypto.createHash("sha256").update(m).digest("hex");fs.writeFileSync(path.join(s,k),m);const w={schemaVersion:2,mediaType:"application/vnd.oci.image.index.v1+json",manifests:[{mediaType:"application/vnd.oci.image.manifest.v1+json",digest:`sha256:${k}`,size:m.length,annotations:{"org.opencontainers.image.ref.name":e}}]};fs.writeJsonSync(path.join(r,"index.json"),w,{spaces:2});const S=e.replace(/[^a-zA-Z0-9_.-]/g,"-"),$=t?path.resolve(t):path.join(process.cwd(),`${S}-oci.tar`);process.stdout.write(chalk.dim(" Writing OCI tar... ")),spawnSync("tar",["-cf",$,"-C",r,"."],{stdio:"pipe"}),console.log(chalk.green("✓"));const x=(fs.statSync($).size/1024/1024).toFixed(1);return console.log(chalk.green(`\n[OK] OCI tar exported: ${$} (${x}MB)`)),console.log(chalk.dim(" Compatible with: kubectl, skopeo, crane, containerd, AWS ECR, GCP Artifact Registry")),fs.removeSync(r),{success:!0,file:$,size:x,digest:`sha256:${k}`}}catch(e){throw fs.removeSync(r),new Error(`OCI export failed: ${e.message}`)}}async pushToECR(e,t){let s,o;console.log(chalk.cyan(`\n[OCI→ECR] Pushing "${e}" to AWS ECR...`));try{const e=execSync("aws ecr get-login-password --region $(aws configure get region || echo us-east-1)",{encoding:"utf8",stdio:"pipe"}).trim();s=Buffer.from(e).toString("base64")}catch(e){throw new Error(`AWS CLI not configured. Run: aws configure\n${e.message}`)}try{o=`https://${execSync("aws sts get-caller-identity --query Account --output text",{encoding:"utf8",stdio:"pipe"}).trim()}.dkr.ecr.${execSync("aws configure get region",{encoding:"utf8",stdio:"pipe"}).trim()||"us-east-1"}.amazonaws.com`}catch(e){throw new Error(`Could not determine ECR registry URL: ${e.message}`)}return console.log(chalk.dim(` Registry: ${o}/${t}`)),this._pushViaSkopeo(e,`${o}/${t}`,{token:s})}async pushToGCR(e,t){let s;console.log(chalk.cyan(`\n[OCI→GCR] Pushing "${e}" to GCP Artifact Registry...`));try{s=execSync("gcloud auth print-access-token",{encoding:"utf8",stdio:"pipe"}).trim()}catch(e){throw new Error(`gcloud CLI not configured. Run: gcloud auth login\n${e.message}`)}return this._pushViaSkopeo(e,t,{token:s})}async pushToDockerHub(e,t,s={}){return console.log(chalk.cyan(`\n[OCI→DockerHub] Pushing "${e}" to Docker Hub...`)),this._pushViaSkopeo(e,t,s)}async pushToGHCR(e,t,s={}){return console.log(chalk.cyan(`\n[OCI→GHCR] Pushing "${e}" to GitHub Container Registry...`)),this._pushViaSkopeo(e,t,s)}async _pushViaSkopeo(e,t,s={}){const o=this._commandExists("skopeo"),r=this._commandExists("crane");if(o){const o=s.token?`--dest-creds "AWS:${s.token}"`:s.username?`--dest-creds "${s.username}:${s.password}"`:"";try{const s=await this.exportTar(e);return execSync(`skopeo copy oci-archive:"${s.file}" docker://${t} ${o}`,{stdio:"inherit"}),{success:!0,pushed:t}}catch(e){throw new Error(`skopeo push failed: ${e.message}`)}}if(r)try{const s=await this.exportTar(e);return execSync(`crane push "${s.file}" ${t}`,{stdio:"inherit"}),{success:!0,pushed:t}}catch(e){throw new Error(`crane push failed: ${e.message}`)}const n=await this.exportTar(e);return console.log(chalk.yellow("\n[OCI] skopeo/crane not installed. OCI tar exported.")),console.log(chalk.dim(" Install skopeo: brew install skopeo (macOS) / apt install skopeo (Linux)")),console.log(chalk.dim(` Then run: skopeo copy oci-archive:${n.file} docker://${t}`)),{success:!1,tarFile:n.file,manual:!0}}_commandExists(e){try{return execSync(`which ${e}`,{stdio:"pipe"}),!0}catch{return!1}}_loadManifest(){const e=path.join(this.imagesBase,"manifest.json"),t=path.join(this.imagesBase,"images.json");let s={},o={};try{s=fs.readJsonSync(e)}catch(e){}try{o=fs.readJsonSync(t)}catch(e){}for(const[e,t]of Object.entries(o))s[e]||(s[e]={...t,rootfsDir:t.rootfsDir||path.join(this.imagesBase,t.id||"","rootfs"),layers:t.layers||1});return s}}module.exports=OCIExporter;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const https=require("https"),http=require("http"),fs=require("fs-extra"),path=require("path"),os=require("os"),crypto=require("crypto"),{execSync,spawnSync}=require("child_process"),chalk=require("chalk"),ImageStore=require("./image-store"),OCI_BASE=path.join(os.homedir(),".tnc","images"),OCI_MANIFEST=path.join(OCI_BASE,"manifest.json"),REGISTRY_MAP={"docker.io":{registry:"https://registry-1.docker.io",auth:"https://auth.docker.io/token",service:"registry.docker.io"},"ghcr.io":{registry:"https://ghcr.io",auth:"https://ghcr.io/token",service:"ghcr.io"},"gcr.io":{registry:"https://gcr.io",auth:null,service:"gcr.io"},"public.ecr.aws":{registry:"https://public.ecr.aws",auth:"https://public.ecr.aws/token",service:"public.ecr.aws"}};class OCIPuller{constructor(){fs.ensureDirSync(OCI_BASE),this.store=new ImageStore,this.manifest=this._loadManifest(),this.hostArch=this._detectArch(),this.hostOS=this._detectOS()}_loadManifest(){try{return fs.readJsonSync(OCI_MANIFEST)}catch{return{}}}_saveManifest(){fs.writeJsonSync(OCI_MANIFEST,this.manifest,{spaces:2})}_detectArch(){const t=os.arch();return"arm64"===t||"aarch64"===t?"arm64":"amd64"}_detectOS(){return process.platform,"linux"}parseImageRef(t){let e="docker.io",s=t,r="latest";const o=t.split("/");if(o.length>=2&&(o[0].includes(".")||o[0].includes(":")||"localhost"===o[0])?(e=o[0],s=o.slice(1).join("/")):1===o.length?s=`library/${t}`:o[0].includes(".")||2!==o.length||(s=t),s.includes(":")){const t=s.lastIndexOf(":");r=s.slice(t+1),s=s.slice(0,t)}s=s.replace(/[^a-zA-Z0-9_\-\.\/]/g,"").replace(/\.\.+/g,""),r=r.replace(/[^a-zA-Z0-9_\-\.]/g,"").replace(/\.\.+/g,""),r||(r="latest");const i=Object.keys(REGISTRY_MAP).find(t=>e.includes(t))||e,n=REGISTRY_MAP[i]||{registry:e.startsWith("http")?e:`https://${e}`,auth:null,service:e};return{registry:e,registryKey:i,registryInfo:n,repo:s,tag:r,fullRef:`${s}:${r}`}}_fetch(t,e={}){return new Promise((s,r)=>{const o=new URL(t),i="https:"===o.protocol?https:http,n={hostname:o.hostname,port:o.port||("https:"===o.protocol?443:80),path:o.pathname+o.search,method:"GET",headers:{"User-Agent":"thinknsh/2.1 (ThinkNCollab-Shell)",...e}},a=i.request(n,t=>{if([301,302,307,308].includes(t.statusCode)&&t.headers.location){const i=t.headers.location,n=new URL(i).hostname,a={...e};return n!==o.hostname&&delete a.Authorization,this._fetch(i,a).then(s).catch(r)}const i=[];t.on("data",t=>i.push(t)),t.on("end",()=>s({status:t.statusCode,headers:t.headers,body:Buffer.concat(i)}))});a.on("error",r),a.setTimeout(3e4,()=>{a.destroy(),r(new Error("Request timeout"))}),a.end()})}_download(t,e,s={},r=""){return new Promise((o,i)=>{const n=new URL(t),a="https:"===n.protocol?https:http,c={hostname:n.hostname,port:n.port||("https:"===n.protocol?443:80),path:n.pathname+n.search,method:"GET",headers:{"User-Agent":"thinknsh/2.1 (ThinkNCollab-Shell)",...s}},l=a.request(c,t=>{if([301,302,307,308].includes(t.statusCode)&&t.headers.location){const a=t.headers.location,c=new URL(a).hostname,l={...s};return c!==n.hostname&&delete l.Authorization,this._download(a,e,l,r).then(o).catch(i)}const a=parseInt(t.headers["content-length"]||"0",10);let c=0;const l=fs.createWriteStream(e);t.on("data",t=>{if(c+=t.length,l.write(t),a>0){const t=Math.round(c/a*100),e="█".repeat(Math.floor(t/5))+"░".repeat(20-Math.floor(t/5));process.stdout.write(`\r ${chalk.dim(r)} [${chalk.cyan(e)}] ${t}% (${(c/1024/1024).toFixed(1)}MB)`)}}),t.on("end",()=>{l.end(),a>0&&process.stdout.write("\n"),o({size:c})}),t.on("error",i),l.on("error",i)});l.on("error",i),l.setTimeout(3e5,()=>{l.destroy(),i(new Error("Download timeout"))}),l.end()})}async _getToken(t,e){if(!t.auth)return null;try{const s=`${t.auth}?service=${t.service}&scope=repository:${e}:pull`,r=await this._fetch(s);if(200===r.status){const t=JSON.parse(r.body.toString());return t.token||t.access_token||null}}catch(t){}return null}async _fetchManifest(t,e,s,r){const o={Accept:["application/vnd.oci.image.manifest.v1+json","application/vnd.docker.distribution.manifest.v2+json","application/vnd.docker.distribution.manifest.list.v2+json","application/vnd.oci.image.index.v1+json"].join(", ")};r&&(o.Authorization=`Bearer ${r}`);const i=`${t.registry}/v2/${e}/manifests/${s}`,n=await this._fetch(i,o);if(401===n.status)throw new Error(`Auth failed for ${e}:${s}. Image may be private or requires login.`);if(404===n.status)throw new Error(`Image ${e}:${s} not found on registry.`);if(200!==n.status)throw new Error(`Registry returned HTTP ${n.status} for ${e}:${s}`);const a=JSON.parse(n.body.toString()),c=a.mediaType||n.headers["content-type"]||"";return c.includes("manifest.list")||c.includes("image.index")||a.manifests?this._selectPlatformManifest(a.manifests,t,e,r):a}async _selectPlatformManifest(t,e,s,r){const o=this.hostOS,i=this.hostArch;let n=t.find(t=>t.platform?.os===o&&t.platform?.architecture===i);if(n||(n=t.find(t=>t.platform?.os===o&&"amd64"===t.platform?.architecture)),n||(n=t[0]),!n)throw new Error("No compatible platform found in multi-arch manifest.");const a=n.platform?.architecture||"unknown",c=n.platform?.os||"unknown";return console.log(chalk.dim(` Platform: ${c}/${a} (host: ${this.hostOS}/${this.hostArch})`)),this._fetchManifest(e,s,n.digest,r)}async pull(t,e={}){const{registryInfo:s,repo:r,tag:o,fullRef:i}=this.parseImageRef(t),n=`${r}:${o}`;try{const t=(new(require("./policy-engine"))).evaluateImage({registry:s.registry,repo:r,tag:o,fullRef:i});if(!t.allowed)throw new Error(`[Security Policy] Image pull denied: ${t.reason}`)}catch(t){if(t.message.includes("[Security Policy]"))throw t}if(console.log(chalk.cyan(`\nPulling ${chalk.bold(n)} from ${s.registry}...`)),this.manifest[n]&&!e.force){const t=this.manifest[n];return console.log(chalk.green(`[OK] Already exists: ${n} (pulled ${new Date(t.pulledAt).toLocaleDateString()})`)),console.log(chalk.dim(" Use --force to re-pull")),{cached:!0,meta:t}}process.stdout.write(chalk.dim(" Authenticating... "));const a=await this._getToken(s,r);console.log(chalk.green("✓")),process.stdout.write(chalk.dim(" Fetching manifest... "));const c=await this._fetchManifest(s,r,o,a);console.log(chalk.green("✓"));const l=`${r.replace(/\//g,"-")}-${o}-${Date.now().toString(36)}`,h=path.join(OCI_BASE,r.replace(/\//g,"-"),o),f=path.join(h,"rootfs");fs.ensureDirSync(f);const d=c.layers||[],p=c.config,g={};a&&(g.Authorization=`Bearer ${a}`),console.log(chalk.dim(` Layers: ${d.length} | Config: ${p?.digest?.slice(0,16)}...`));for(let t=0;t<d.length;t++){const e=d[t],o=e.digest,i=o.slice(7,19),n=((e.size||0)/1024/1024).toFixed(1),a=(path.join(h,`layer-${t}.tar.gz`),o.replace(/^sha256:/,"")),c=this.store.getBlobPath(a);if(this.store.hasBlob(a))console.log(chalk.dim(` Layer ${t+1}/${d.length}: ${i} (${n}MB) — CAS cached ✓`));else{const l=`${s.registry}/v2/${r}/blobs/${o}`;await this._download(l,c,g,`Layer ${t+1}/${d.length}: ${i} (${n}MB)`),this.store.putBlob(a,c,e.size||0,e.mediaType)}let l=!1;const p=path.join(os.homedir(),".tnc","vmctl.sock");if(fs.existsSync(p))try{const{sendCommand:t}=require("./microvm-manager"),s=await t({cmd:"extract",blob:c,dest:f,mediaType:e.mediaType||""},3e4);s&&s.ok&&(l=!0)}catch(t){}if(!l)try{const t=(e.mediaType||"").includes("zstd")?["--use-compress-program=zstd","-xf",c,"-C",f]:["-xzf",c,"-C",f],s=spawnSync("tar",t,{stdio:"pipe"});if(0!==s.status&&s.stderr){const t=s.stderr.toString();t.includes("Ignoring unknown extended header keyword")||t.includes("Cannot change ownership")||console.warn(chalk.dim(` [Extract] host tar notice: ${t.trim().split("\n")[0]}`))}}catch(t){console.warn(chalk.yellow(` [Extract] host tar error: ${t.message}`))}try{this._processWhiteouts(f)}catch(t){}}let u={};if(p?.digest)try{const t=await this._fetch(`${s.registry}/v2/${r}/blobs/${p.digest}`,g);u=JSON.parse(t.body.toString())}catch(t){}const m={id:l,repo:r,tag:o,imageKey:n,imageDir:h,rootfsDir:f,registry:s.registry,platform:`${this.hostOS}/${this.hostArch}`,layers:d.length,entrypoint:u.config?.Entrypoint||[],cmd:u.config?.Cmd||[],env:u.config?.Env||[],exposedPorts:u.config?.ExposedPorts||{},workingDir:u.config?.WorkingDir||"/",labels:u.config?.Labels||{},size:d.reduce((t,e)=>t+(e.size||0),0),pulledAt:(new Date).toISOString()};this.manifest[n]=m,this._saveManifest(),this.store.registerImage({repo:r,tag:o,digest:p?.digest||null,platform:m.platform,sizeBytes:m.size,layersCount:d.length,rootfsDir:f,config:u,blobDigests:d.map(t=>t.digest)}),fs.writeJsonSync(path.join(h,"config.json"),u,{spaces:2}),fs.writeJsonSync(path.join(h,"meta.json"),m,{spaces:2});const y=(m.size/1024/1024).toFixed(1);return console.log(chalk.green(`\n[OK] Pulled ${chalk.bold(n)} (${y}MB, ${d.length} layers)`)),console.log(chalk.dim(` Stored at: ${h}`)),(m.entrypoint.length||m.cmd.length)&&console.log(chalk.dim(` Entrypoint: ${[...m.entrypoint,...m.cmd].join(" ")}`)),{cached:!1,meta:m}}list(){const t=new Map;try{const e=this.store.listImages();for(const s of e)t.set(s.fullRef||`${s.repo}:${s.tag}`,s)}catch(t){}for(const[e,s]of Object.entries(this.manifest||{})){if(!s||!s.id)continue;let e=s.repo,r=s.tag;e||(s.tag&&s.tag.includes(":")?[e,r]=s.tag.split(":"):(e=s.project||s.tag||"local",r="latest"));const o=`${e}:${r}`;t.has(o)||t.set(o,{id:s.id,repo:e,tag:r,fullRef:o,platform:s.platform||"linux/arm64",layers:s.layers||1,sizeMB:s.sizeMB||((s.size||0)/1024/1024).toFixed(1),pulledAt:s.pulledAt||s.createdAt,imageDir:s.imageDir,rootfsDir:s.rootfsDir})}return Array.from(t.values())}tag(t,e){return this.store.tagImage(t,e)}prune(){return this.store.pruneBlobs()}remove(t){try{this.store.removeImage(t)}catch(t){}const{repo:e,tag:s}=this.parseImageRef(t),r=`${e}:${s}`,o=this.manifest[r]||this.manifest[t];if(o){if(o.imageDir&&fs.existsSync(o.imageDir))try{fs.removeSync(o.imageDir)}catch(t){}delete this.manifest[r],delete this.manifest[t],this._saveManifest()}return{success:!0}}inspect(t){const e=this.store.getImage(t);if(e)return e;const{repo:s,tag:r}=this.parseImageRef(t),o=`${s}:${r}`;if(this.manifest[o])return this.manifest[o];if(this.manifest[t])return this.manifest[t];for(const e of Object.values(this.manifest))if(e&&(e.id===t||e.tag===t||e.fullRef===t))return e;return null}_processWhiteouts(t){if(fs.existsSync(t))try{const e=fs.readdirSync(t,{withFileTypes:!0});for(const s of e){const e=path.join(t,s.name);if(s.isDirectory())this._processWhiteouts(e);else if(".wh..wh..opq"===s.name)try{fs.unlinkSync(e)}catch(t){}else if(s.name.startsWith(".wh.")){const r=s.name.slice(4),o=path.join(t,r);try{fs.existsSync(o)&&fs.removeSync(o),fs.unlinkSync(e)}catch(t){}}}}catch(t){}}}module.exports=OCIPuller;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),os=require("os"),DEFAULT_CAPS=["CAP_CHOWN","CAP_DAC_OVERRIDE","CAP_FSETID","CAP_FOWNER","CAP_MKNOD","CAP_NET_RAW","CAP_SETGID","CAP_SETUID","CAP_SETFCAP","CAP_SETPCAP","CAP_NET_BIND_SERVICE","CAP_SYS_CHROOT","CAP_KILL","CAP_AUDIT_WRITE"];class OCIRuntimeSpec{static generate({rootfsPath:e,cmd:t=["/bin/sh"],env:s=[],workingDir:o="/",memoryLimitBytes:i=2147483648,cpuQuota:n=0,pidsLimit:r=1e3,mounts:c=[],isRootless:p=!0,terminal:a=!0}){const d=["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin","TERM=xterm-256color",...s],m=c.map(e=>({destination:e.destination,type:e.type||"none",source:e.source,options:e.options||["bind","rw"]})),l={ociVersion:"1.0.2",process:{terminal:a,user:{uid:0,gid:0},args:Array.isArray(t)?t:[t],env:d,cwd:o||"/",capabilities:{bounding:[...DEFAULT_CAPS],effective:[...DEFAULT_CAPS],inheritable:[...DEFAULT_CAPS],permitted:[...DEFAULT_CAPS],ambient:[]},rlimits:[{type:"RLIMIT_NOFILE",hard:65536,soft:65536}],noNewPrivileges:!0},root:{path:e,readonly:!1},hostname:"tnc-sandbox",mounts:[{destination:"/proc",type:"proc",source:"proc",options:["nosuid","noexec","nodev"]},{destination:"/dev",type:"tmpfs",source:"tmpfs",options:["nosuid","strictatime","mode=755","size=65536k"]},{destination:"/dev/pts",type:"devpts",source:"devpts",options:["nosuid","noexec","newinstance","ptmxmode=0666","mode=0620"]},{destination:"/dev/shm",type:"tmpfs",source:"shm",options:["nosuid","noexec","nodev","mode=1777","size=65536k"]},{destination:"/sys",type:"sysfs",source:"sysfs",options:["nosuid","noexec","nodev","ro"]},...m],linux:{resources:{memory:{limit:i,swap:i},pids:{limit:r}},namespaces:[{type:"pid"},{type:"ipc"},{type:"uts"},{type:"mount"}],seccomp:{defaultAction:"SCMP_ACT_ERRNO",architectures:["arm64"===process.arch?"SCMP_ARCH_AARCH64":"SCMP_ARCH_X86_64"],syscalls:[{names:["read","write","open","openat","close","stat","fstat","lstat","poll","lseek","mmap","mprotect","munmap","brk","rt_sigaction","rt_sigprocmask","ioctl","access","pipe","select","sched_yield","nanosleep","getpid","socket","connect","accept","sendto","recvfrom","bind","listen","getsockname","getpeername","clone","fork","vfork","execve","exit","wait4","kill","uname","fcntl","flock","fsync","getdents","getdents64","getcwd","chdir","mkdir","rmdir","unlink","unlinkat","rename","renameat","readlink","readlinkat","chmod","fchmod","chown","fchown","gettimeofday","clock_gettime","futex","epoll_create","epoll_create1","epoll_ctl","epoll_wait","epoll_pwait"],action:"SCMP_ACT_ALLOW"}]}}};return n>0&&(l.linux.resources.cpu={quota:n,period:1e5}),p&&(l.linux.namespaces.push({type:"user"}),l.linux.uidMappings=[{hostID:process.getuid?process.getuid():1e3,containerID:0,size:1}],l.linux.gidMappings=[{hostID:process.getgid?process.getgid():1e3,containerID:0,size:1}]),l}}module.exports=OCIRuntimeSpec;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const fs=require("fs-extra"),path=require("path"),os=require("os"),chalk=require("chalk"),POLICY_FILE=path.join(os.homedir(),".tnc","policy.json"),DEFAULT_POLICY={enabled:!1,strictDigestPinning:!1,allowedRegistries:["docker.io","ghcr.io","gcr.io","public.ecr.aws"],blockedRegistries:[],allowedImages:["*"],blockedImages:[],pinnedDigests:{}};class PolicyEngine{constructor(){this.policy=this._loadPolicy()}_loadPolicy(){try{if(fs.existsSync(POLICY_FILE))return{...DEFAULT_POLICY,...fs.readJsonSync(POLICY_FILE)}}catch(i){}return{...DEFAULT_POLICY}}_savePolicy(){try{fs.ensureDirSync(path.dirname(POLICY_FILE)),fs.writeJsonSync(POLICY_FILE,this.policy,{spaces:2})}catch(i){}}getPolicy(){return{...this.policy}}setPolicy(i={}){return this.policy={...this.policy,...i},this._savePolicy(),this.policy}evaluateImage(i){if(!this.policy.enabled)return{allowed:!0};const{registry:e,repo:s,tag:t,fullRef:l}=i,o=`${s}:${t}`;if(this.policy.blockedRegistries?.some(i=>e.includes(i)))return{allowed:!1,reason:`Registry "${e}" is blocked by security policy.`};if(this.policy.allowedRegistries&&this.policy.allowedRegistries.length>0&&!this.policy.allowedRegistries.some(i=>"*"===i||e.includes(i)))return{allowed:!1,reason:`Registry "${e}" is not in the allowed registries list: [${this.policy.allowedRegistries.join(", ")}]`};if(this.policy.blockedImages?.some(i=>this._matchPattern(o,i)))return{allowed:!1,reason:`Image "${o}" is explicitly denied by team policy.`};if(this.policy.allowedImages&&this.policy.allowedImages.length>0&&!this.policy.allowedImages.includes("*")&&!this.policy.allowedImages.some(i=>this._matchPattern(o,i)))return{allowed:!1,reason:`Image "${o}" is not in the authorized image whitelist.`};const n=this.policy.pinnedDigests?.[o]||this.policy.pinnedDigests?.[s];return this.policy.strictDigestPinning&&!n?{allowed:!1,reason:`Strict digest pinning enabled: image "${o}" does not have an approved immutable SHA256 digest.`}:{allowed:!0,pinnedDigest:n||null}}pinDigest(i,e){this.policy.pinnedDigests||(this.policy.pinnedDigests={}),this.policy.pinnedDigests[i]=e,this._savePolicy()}_matchPattern(i,e){return"*"===e||i===e||!!e.endsWith("*")&&i.startsWith(e.slice(0,-1))}}module.exports=PolicyEngine;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ "use strict";const net=require("net"),path=require("path"),fs=require("fs-extra"),os=require("os"),sandboxId=process.argv[2],hostPort=parseInt(process.argv[3],10),containerPort=parseInt(process.argv[4],10),targetIp=process.argv[5]||"192.168.64.2";(!sandboxId||isNaN(hostPort)||isNaN(containerPort))&&(console.error("Usage: node port-proxy-daemon.js <sandboxId> <hostPort> <containerPort> [targetIp]"),process.exit(1));const sandboxDir=path.join(os.homedir(),".tnc","sandboxes",sandboxId);fs.ensureDirSync(sandboxDir);const pidFile=path.join(sandboxDir,"proxy.pid");fs.writeFileSync(pidFile,String(process.pid),"utf8");const server=net.createServer(e=>{const r=net.createConnection({host:targetIp,port:containerPort});e.pipe(r),r.pipe(e),r.on("error",()=>e.destroy()),e.on("error",()=>r.destroy()),r.on("close",()=>e.destroy()),e.on("close",()=>r.destroy())});let listenAddress="127.0.0.1";try{listenAddress=require("../../dock/config").effective().publishAddress||listenAddress}catch(e){}function cleanup(){try{fs.existsSync(pidFile)&&fs.removeSync(pidFile)}catch(e){}try{server.close()}catch(e){}process.exit(0)}server.listen(hostPort,listenAddress,()=>{}),server.on("error",e=>{console.error(`[Proxy] Error binding port ${hostPort}: ${e.message}`),cleanup()}),process.on("SIGINT",cleanup),process.on("SIGTERM",cleanup),process.on("SIGHUP",cleanup),process.on("exit",()=>{try{fs.existsSync(pidFile)&&fs.removeSync(pidFile)}catch(e){}});
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),os=require("os"),fs=require("fs-extra"),http=require("http"),https=require("https"),{spawnSync}=require("child_process"),crypto=require("crypto"),chalk=require("chalk"),e2eeVault=require("../crypto/e2ee-vault"),ROOMS_BASE=path.join(os.homedir(),".tnc","rooms"),DATA_DIR=path.join(os.homedir(),".tnc","data");global._tncP2pServers||(global._tncP2pServers=new Map);class RoomSyncManager{constructor(){fs.ensureDirSync(ROOMS_BASE)}getRoomDir(e){const t=e.replace(/[^a-zA-Z0-9_-]/g,"-"),o=path.join(ROOMS_BASE,t);return fs.ensureDirSync(o),o}_findBinary(e){try{const t=spawnSync("which",[e],{stdio:"pipe"});if(0===t.status)return t.stdout.toString().trim()}catch(e){}for(const t of["/opt/homebrew/bin","/usr/local/bin","/usr/bin"]){const o=path.join(t,e);if(fs.existsSync(o))return o}return e}_validateAndSanitizeExtracted(e,t){const o=fs.readdirSync(e,{withFileTypes:!0});for(const n of o){const o=path.join(e,n.name);if(n.isSymbolicLink())try{const e=fs.realpathSync(o),s=path.relative(t,e);if(s.startsWith("..")||path.isAbsolute(s)&&!e.startsWith(t))throw new Error(`Path traversal / symlink escape detected: "${n.name}" targets "${e}" outside snapshot root`)}catch(e){if(e.message.includes("Path traversal"))throw e;try{fs.removeSync(o)}catch(e){}continue}try{const e=fs.lstatSync(o);e.isSymbolicLink()||(3072&e.mode&&fs.chmodSync(o,-3073&e.mode),n.isDirectory()&&this._validateAndSanitizeExtracted(o,t))}catch(e){}}}_getIgnoreRules(e){const t=new Set(["node_modules",".git",".tnc",".DS_Store","logs","scratch","tmp","coverage",".next",".nuxt",".turbo",".cache"]);for(const o of[".ignoretnc",".gitignore"]){const n=path.join(e,o);if(fs.existsSync(n))try{const e=fs.readFileSync(n,"utf8").split(/\r?\n/);for(let o of e)o=o.trim(),o&&!o.startsWith("#")&&(o=o.replace(/\/+$/,""),o&&t.add(o))}catch(e){}}return t}_isBinaryFile(e){try{const t=fs.openSync(e,"r"),o=Buffer.alloc(4096),n=fs.readSync(t,o,0,4096,0);fs.closeSync(t);for(let e=0;e<n;e++)if(0===o[e])return!0;return!1}catch(e){return!1}}_mergePackageJson(e,t){try{const o=fs.readJsonSync(e),n=fs.readJsonSync(t);if(n.dependencies){o.dependencies=o.dependencies||{};for(const[e,t]of Object.entries(n.dependencies))o.dependencies[e]||(o.dependencies[e]=t)}if(n.devDependencies){o.devDependencies=o.devDependencies||{};for(const[e,t]of Object.entries(n.devDependencies))o.devDependencies[e]||(o.devDependencies[e]=t)}if(n.scripts){o.scripts=o.scripts||{};for(const[e,t]of Object.entries(n.scripts))o.scripts[e]||(o.scripts[e]=t)}return fs.writeJsonSync(e,o,{spaces:2}),!0}catch(e){return!1}}_mergeEnvFile(e,t,o){try{const n=fs.readFileSync(e,"utf8"),s=fs.readFileSync(t,"utf8"),a=new Set;for(const e of n.split(/\r?\n/)){const t=e.trim();if(t&&!t.startsWith("#")){const e=t.indexOf("=");e>0&&a.add(t.substring(0,e).trim())}}const r=[];for(const e of s.split(/\r?\n/)){const t=e.trim();if(t&&!t.startsWith("#")){const e=t.indexOf("=");if(e>0){const o=t.substring(0,e).trim();a.has(o)||(r.push(t),a.add(o))}}}if(r.length>0){const t=`\n# ── Synced from ${o||"teammate"} (${(new Date).toLocaleDateString()}) ──\n`;return fs.appendFileSync(e,t+r.join("\n")+"\n","utf8"),{addedCount:r.length,keys:r.map(e=>e.split("=")[0])}}return{addedCount:0,keys:[]}}catch(e){return{addedCount:0,keys:[]}}}_mergeWorkspaceFiles(e,t,o={}){const n={added:[],cleanMerged:[],conflicts:[],unchanged:[],updated:[]},s=o.roomId,a=o.author||"teammate",r=o.localUser||"Local",c=o.overwrite||!1,i=o.baseDir&&fs.existsSync(o.baseDir)?o.baseDir:null,l=`backup-${Date.now().toString(36)}`,d=path.join(os.homedir(),".tnc","backups",`${s||"room"}-${l}`);let h=!1;const p=(t,o=[])=>{const n=fs.readdirSync(t,{withFileTypes:!0});for(const s of n){const n=path.join(t,s.name),a=path.relative(e,n),r=a.split(path.sep);r.includes(".git")||r.includes(".tnc")||r.includes("node_modules")||(s.isDirectory()?p(n,o):s.isFile()&&o.push({full:n,rel:a}))}return o},y=p(e),f=path.join(os.tmpdir(),`tnc_empty_base_${Date.now()}`);fs.writeFileSync(f,"");try{for(const{full:e,rel:o}of y){const s=path.join(t,o);if(!fs.existsSync(s)){fs.ensureDirSync(path.dirname(s)),fs.copyFileSync(e,s),n.added.push(o);continue}const l=fs.readFileSync(e),p=fs.readFileSync(s);if(l.equals(p)){n.unchanged.push(o);continue}try{const e=path.join(d,o);fs.ensureDirSync(path.dirname(e)),fs.copyFileSync(s,e),h=!0}catch(e){}if(c){fs.copyFileSync(e,s),n.updated.push(o);continue}if("package.json"===o&&this._mergePackageJson(s,e)){n.cleanMerged.push(o);continue}if(this._isBinaryFile(e)||this._isBinaryFile(s)){const s=path.join(t,`${o}.incoming`);fs.copyFileSync(e,s),n.updated.push(`${o} (saved as .incoming)`);continue}let y=f;if(i){const e=path.join(i,o);fs.existsSync(e)&&(y=e)}0===spawnSync("git",["merge-file","-L",`Local (${r})`,"-L","Base","-L",`Incoming (${a})`,s,y,e],{stdio:"pipe"}).status?n.cleanMerged.push(o):n.conflicts.push(o)}}finally{try{fs.unlinkSync(f)}catch(e){}}if(h){n.backupDir=d;try{const e=this.getRoomDir(s);fs.writeJsonSync(path.join(e,"last_backup.json"),{backupDir:d,createdAt:(new Date).toISOString(),roomId:s})}catch(e){}}return n}rollbackLastMerge(e,t){const o=this.getRoomDir(e),n=path.join(o,"last_backup.json");if(!fs.existsSync(n))throw new Error("No previous merge backup found for this room.");const{backupDir:s}=fs.readJsonSync(n);if(!s||!fs.existsSync(s))throw new Error(`Backup directory no longer exists: ${s}`);return console.log(chalk.cyan(`\n[RoomSync] Rolling back to pre-merge snapshot: ${s}...`)),fs.copySync(s,t,{overwrite:!0}),console.log(chalk.green("[OK] Workspace successfully rolled back to pre-merge state!\n")),{success:!0}}async _captureMongoSnapshot(e,t){let o=process.env.MONGODB_URI||process.env.MONGO_URI;if(!o){const e=path.join(t,".env");if(fs.existsSync(e))try{const t=fs.readFileSync(e,"utf8").match(/^(?:MONGODB_URI|MONGO_URI)\s*=\s*["']?([^"'\r\n]+)["']?/m);t&&t[1]&&(o=t[1].trim())}catch(e){}}if(!o)return null;let n=null;try{const e=path.join(t,"node_modules","mongodb");fs.existsSync(e)&&(n=require(e).MongoClient)}catch(e){}if(!n)try{n=require("mongodb").MongoClient}catch(e){}if(!n)return null;console.log(chalk.dim(" Capturing MongoDB database snapshot (Atlas / Local)..."));let s=null;try{s=new n(o,{serverSelectionTimeoutMS:5e3,connectTimeoutMS:5e3}),await s.connect();const t=s.db(),a=new Set(["sessions","pagepathhits","pageviews","adminauditlogs","webhooklogs"]),r=(await t.listCollections().toArray()).filter(e=>!a.has(e.name)),c=path.join(e,"data","mongodb");fs.ensureDirSync(c);let i=0,l=0;const d=10;for(let e=0;e<r.length;e+=d){const o=r.slice(e,e+d);await Promise.all(o.map(async e=>{try{const o=await t.collection(e.name).find({}).limit(5e3).toArray();o.length>0&&(fs.writeJsonSync(path.join(c,`${e.name}.json`),o),i+=o.length,l++)}catch(e){}}))}if(l>0)return console.log(chalk.green(` ✓ Captured MongoDB snapshot (${l} collections, ${i} documents)!`)),{collections:l,documents:i}}catch(e){console.log(chalk.yellow(` [Notice] Could not capture MongoDB snapshot: ${e.message}`))}finally{if(s)try{await s.close()}catch(e){}}return null}async _restoreMongoSnapshot(e,t){let o=process.env.MONGODB_URI||process.env.MONGO_URI;if(!o){const e=path.join(t,".env");if(fs.existsSync(e))try{const t=fs.readFileSync(e,"utf8").match(/^(?:MONGODB_URI|MONGO_URI)\s*=\s*["']?([^"'\r\n]+)["']?/m);t&&t[1]&&(o=t[1].trim())}catch(e){}}const n=o||"mongodb://127.0.0.1:27017/thinkncollab_dev";let s=null;try{const e=path.join(t,"node_modules","mongodb");fs.existsSync(e)&&(s=require(e).MongoClient)}catch(e){}if(!s)try{s=require("mongodb").MongoClient}catch(e){}const a=fs.readdirSync(e).filter(e=>e.endsWith(".json"));if(0===a.length)return;let r=!1;if(s){let t=null;try{t=new s(n,{serverSelectionTimeoutMS:3e3,connectTimeoutMS:3e3}),await t.connect();const o=t.db();let c=0;for(const t of a){const n=path.basename(t,".json"),s=fs.readJsonSync(path.join(e,t));if(Array.isArray(s)&&s.length>0)try{await o.collection(n).deleteMany({}),await o.collection(n).insertMany(s),c+=s.length}catch(e){}}console.log(chalk.green(` ✓ Restored MongoDB database (${a.length} collections, ${c} documents)!`)),r=!0}catch(e){}finally{if(t)try{await t.close()}catch(e){}}}if(!r){const o=path.join(t,".tnc-mongo-dump");fs.copySync(e,o,{overwrite:!0}),console.log(chalk.cyan(` ✓ Saved MongoDB snapshot to .tnc-mongo-dump/ (${a.length} collections ready for import)`))}}async _captureRedisSnapshot(e,t){const o=path.join(e,"data","redis"),n=path.join(DATA_DIR,"redis"),s=path.join(n,"dump.rdb");if(fs.existsSync(s)||fs.existsSync(n)){try{const e=this._findBinary("redis-cli");spawnSync(e,["bgsave"],{stdio:"pipe",timeout:5e3})}catch(e){}if(fs.existsSync(s))return console.log(chalk.dim(" Capturing native Redis database snapshot (dump.rdb)...")),fs.ensureDirSync(o),fs.copyFileSync(s,path.join(o,"dump.rdb")),!0}let a=process.env.REDIS_URL;if(!a){const e=path.join(t,".env");if(fs.existsSync(e))try{const t=fs.readFileSync(e,"utf8").match(/^(?:REDIS_URL)\s*=\s*["']?([^"'\r\n]+)["']?/m);t&&t[1]&&(a=t[1].trim())}catch(e){}}if(a){let e=null;try{const o=path.join(t,"node_modules","ioredis");fs.existsSync(o)&&(e=require(o))}catch(e){}if(!e)try{e=require("ioredis")}catch(e){}if(e){let t=null;try{t=new e(a,{connectTimeout:3e3,lazyConnect:!0}),await t.connect();try{await t.bgsave()}catch(e){}const n=await t.keys("*");if(n.length>0){console.log(chalk.dim(` Capturing Redis in-memory cache (${n.length} keys)...`)),fs.ensureDirSync(o);const e=[];for(const o of n.slice(0,1e3))try{const n=await t.pttl(o),s=await t.dump(o);s&&e.push({k:o,ttl:n>0?n:0,v:s})}catch(e){}return fs.writeJsonSync(path.join(o,"keys.json"),e),console.log(chalk.green(` ✓ Captured Redis cache (${e.length} keys)!`)),!0}}catch(e){}finally{if(t)try{t.disconnect()}catch(e){}}}}return!1}_getLocalIp(){const e=os.networkInterfaces();for(const t of Object.keys(e))for(const o of e[t])if("IPv4"===o.family&&!o.internal)return o.address;return"127.0.0.1"}_startEphemeralP2PServer(e,t){return new Promise((o,n)=>{try{const s=crypto.randomBytes(16).toString("hex"),a=this._getLocalIp(),r=http.createServer((o,n)=>{try{const a=new URL(o.url,`http://${o.headers.host||"localhost"}`);if(a.pathname===`/p2p/state/${e}`){const e=o.headers["x-tnc-sync-token"]||a.searchParams.get("token"),r=Buffer.from(String(e||"")),c=Buffer.from(String(s));if(r.length!==c.length||!crypto.timingSafeEqual(r,c))return n.writeHead(403,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Invalid or expired P2P sync token"}));if(!fs.existsSync(t))return n.writeHead(404,{"Content-Type":"application/json"}),n.end(JSON.stringify({error:"Bundle file no longer exists"}));const i=fs.statSync(t);n.writeHead(200,{"Content-Type":"application/octet-stream","Content-Length":i.size,"X-ThinkNCollab-Encrypted":"true","X-ThinkNCollab-P2P":"true","Cache-Control":"no-cache, no-store"}),console.log(chalk.dim(`\n[RoomSync:P2P] Teammate connected! Streaming AES-256-GCM encrypted state bundle (${(i.size/1024/1024).toFixed(2)} MB)...`));const l=fs.createReadStream(t);return l.pipe(n),void l.on("end",()=>{console.log(chalk.green("[RoomSync:P2P] ✓ Direct peer-to-peer encrypted transfer completed successfully!"))})}n.writeHead(404),n.end()}catch(e){console.error("[RoomSync:P2P] Server error:",e.message),n.writeHead(500),n.end()}}),c=()=>{const t=r.address().port,n={localIp:a,port:t,token:s,server:r};global._tncP2pServers.set(e,n),setTimeout(()=>{try{r.close()}catch(e){}global._tncP2pServers.delete(e)},72e5),o(n)};let i=!1;r.on("error",e=>{i||"EADDRNOTAVAIL"!==e.code&&"EINVAL"!==e.code?n(e):(i=!0,r.listen(0,"0.0.0.0",c))}),r.listen(0,a,c)}catch(e){n(e)}})}_uploadToRelay(e,t,o,n){return new Promise((s,a)=>{try{if(!fs.existsSync(o))return a(new Error(`Bundle file not found: ${o}`));const r=(n?.api?.apiUrl||process.env.THINKNCOLLAB_API_URL||"https://thinkncollab.com").replace(/\/$/,""),c=new URL(`${r}/thinknsh/${e}/cli/relay/${t}`),i="https:"===c.protocol,l=i?https:http,d=n?.api?.getToken?.()||"",h=fs.statSync(o);console.log(chalk.cyan(`\n[RoomSync:Relay] Off-LAN teammate requested state. Streaming AES-256-GCM bundle to in-memory relay (${(h.size/1024/1024).toFixed(2)} MB)...`));const p=l.request({hostname:c.hostname,port:c.port||(i?443:80),path:c.pathname+c.search,method:"POST",headers:{"Content-Type":"application/octet-stream","Content-Length":h.size,"X-ThinkNCollab-Encrypted":"true","x-shell-token":d}},e=>{let t="";e.on("data",e=>t+=e),e.on("end",()=>{if(e.statusCode>=400){let o=t;try{o=JSON.parse(t).error||t}catch(e){}413===e.statusCode?o="State bundle exceeds server relay capacity of 500 MB. Use Direct LAN P2P.":429===e.statusCode&&(o="Room relay limit reached (Max 2 concurrent active relays per room)."),console.error(chalk.red(`[RoomSync:Relay] Upload failed with HTTP ${e.statusCode}: ${o}`)),a(new Error(`Relay upload failed: ${o}`))}else console.log(chalk.green("[RoomSync:Relay] ✓ Encrypted relay stream completed for teammate!")),s()})});p.on("error",e=>{console.error(chalk.red(`[RoomSync:Relay] Network error: ${e.message}`)),a(e)}),fs.createReadStream(o).pipe(p)}catch(e){a(e)}})}async publishState(e,t,o={}){const n=this.getRoomDir(e),s=path.join(n,"states");fs.ensureDirSync(s);const a=t?.api?.getUser?.()||{name:os.userInfo().username,id:"local-user"},r=o.name||`state-${Date.now().toString(36)}`,c=`${r}-${crypto.randomBytes(3).toString("hex")}`,i=path.join(n,"staging",c);fs.ensureDirSync(i),console.log(chalk.cyan(`\n[RoomSync] Preparing encrypted environment state bundle for Room ${chalk.bold(e)}...`));const l=o.cwd||process.cwd(),d={id:c,name:r,roomId:e,author:a.name||a.username||"teammate",authorId:a.id||a._id||"unknown",createdAt:(new Date).toISOString(),description:o.description||"Live development state",services:[],files:[],encrypted:!0,cipher:"aes-256-gcm"};for(const e of["thinkncollab.yml","thinkncollab.yaml","package.json"]){const t=path.join(l,e);fs.existsSync(t)&&(fs.copySync(t,path.join(i,e)),d.files.push(e))}const h=path.join(l,".env");if(fs.existsSync(h)){const e=fs.readFileSync(h,"utf8");fs.writeFileSync(path.join(i,".env.shared"),e,"utf8"),d.files.push(".env.shared")}if(!1!==o.includeCode&&!o.noCode){console.log(chalk.dim(" Packaging project workspace files (skipping node_modules, git, and logs)..."));const e=path.join(i,"workspace");fs.ensureDirSync(e);const t=this._getIgnoreRules(l);let o=0;try{fs.copySync(l,e,{filter:e=>{const n=path.relative(l,e);if(!n)return!0;const s=n.split(path.sep);for(const e of s)if(t.has(e))return!1;const a=path.basename(e);return!(a.endsWith(".tar.gz")||a.endsWith(".tar.gz.enc")||a.endsWith(".log")||".env"===a||".env.room"===a||(o++,0))}}),d.files.push("workspace"),d.hasCode=!0,console.log(chalk.dim(` ✓ Packaged workspace project tree (${o} items)`))}catch(e){console.log(chalk.yellow(` [Notice] Non-fatal error copying workspace files: ${e.message}`))}}const p=path.join(DATA_DIR,"postgres");if(fs.existsSync(p)&&fs.existsSync(path.join(p,"PG_VERSION"))){console.log(chalk.dim(" Issuing native PostgreSQL CHECKPOINT to guarantee clean disk consistency..."));try{const e=this._findBinary("psql");spawnSync(e,["-U","postgres","-c","CHECKPOINT;"],{stdio:"pipe",timeout:5e3})}catch(e){}console.log(chalk.dim(" Capturing native PostgreSQL database snapshot..."));const e=path.join(i,"data","postgres");fs.ensureDirSync(e),"darwin"===process.platform?spawnSync("cp",["-cR",`${p}/.`,`${e}/`],{stdio:"pipe"}):spawnSync("cp",["--reflink=auto","-a",`${p}/.`,`${e}/`],{stdio:"pipe"}),d.services.push("postgres")}try{await this._captureRedisSnapshot(i,l)&&d.services.push("redis")}catch(e){}try{await this._captureMongoSnapshot(i,l)&&d.services.push("mongodb")}catch(e){}fs.writeJsonSync(path.join(i,"manifest.json"),d,{spaces:2});const y=path.join(s,`${c}.tar.gz`);console.log(chalk.dim(` Packaging bundle: ${path.basename(y)}...`)),spawnSync("tar",["-czf",y,"-C",i,"."],{stdio:"pipe"});try{fs.removeSync(i)}catch(e){}const f=path.join(s,`${c}.tar.gz.enc`);console.log(chalk.dim(" Encrypting state bundle with Room AES-256-GCM key...")),await e2eeVault.encryptFile(y,f,e);const u=((fs.statSync(f).size||0)/1024/1024).toFixed(2);d.bundleSizeMB=u,d.bundlePath=f;let m=null;try{m=await this._startEphemeralP2PServer(c,f),d.p2p={localIp:m.localIp,port:m.port,token:m.token},console.log(chalk.dim(` Direct LAN P2P endpoint active: http://${m.localIp}:${m.port}`))}catch(e){console.log(chalk.yellow(` P2P server setup skipped: ${e.message}`))}const g=path.join(n,"index.json"),S=fs.existsSync(g)?fs.readJsonSync(g):[];if(S.unshift(d),fs.writeJsonSync(g,S,{spaces:2}),t?.api?.isAuthenticated?.())try{await t.api._request("POST",`/thinknsh/${e}/cli/states`,{stateId:c,name:r,author:d.author,services:d.services,sizeMB:u,description:d.description,p2p:d.p2p,encrypted:!0,cipher:"aes-256-gcm"}),console.log(chalk.dim(" Registered state metadata with ThinkNCollab room cloud"))}catch(e){console.log(chalk.yellow(` Server state registration note: ${e.message}`))}if(t?.ws?.isConnected?.())try{t.ws.socket.emit("thinknsh:room-state-published",{roomId:e,stateId:c,name:r,author:d.author,sizeMB:u,services:d.services,p2p:d.p2p,encrypted:!0}),t.ws.socket.off("thinknsh:relay-upload-start"),t.ws.socket.on("thinknsh:relay-upload-start",o=>{o?.stateId===c&&this._uploadToRelay(e,o.transferId,f,t).catch(e=>{console.error("[RoomSync:Relay] Upload handler error:",e.message)})})}catch(e){}return console.log(chalk.green(`\n[OK] State published to Room [${e}] successfully!`)),console.log(chalk.dim(` State ID: ${c}`)),console.log(chalk.dim(" Cipher: AES-256-GCM (Zero plaintext over LAN/Relay)")),console.log(chalk.dim(` Bundle Size: ${u} MB (Zero Docker Hub needed)`)),console.log(chalk.dim(` Services: ${d.services.join(", ")||"Node runtime"}`)),console.log(chalk.cyan("\nTeammates in this room can pull this state instantly:")),console.log(chalk.bold(` thinknsh room pull ${c}\n`)),d}_downloadStream(e,t,o={}){return new Promise((n,s)=>{try{const a=new URL(e),r="https:"===a.protocol,c=r?https:http,i={hostname:a.hostname,port:a.port||(r?443:80),path:a.pathname+a.search,method:"GET",headers:{...o},timeout:5e3},l=c.request(i,e=>{if(e.statusCode>=400)return 413===e.statusCode?s(new Error("HTTP 413: State bundle exceeds maximum server relay limit (500 MB)")):429===e.statusCode?s(new Error("HTTP 429: Room relay limit reached (Max 2 concurrent active relays per room)")):s(new Error(`HTTP ${e.statusCode}: Failed to fetch stream`));const o=fs.createWriteStream(t);e.pipe(o),o.on("finish",()=>{o.close(),n()}),o.on("error",e=>{try{fs.removeSync(t)}catch(e){}s(e)})});l.on("timeout",()=>{l.destroy(new Error("Connection timed out"))}),l.on("error",e=>{s(e)}),l.end()}catch(e){s(e)}})}async pullState(e,t,o,n={}){const s=n.cwd||process.cwd(),a=this.getRoomDir(e),r=path.join(a,"states");fs.ensureDirSync(r),console.log(chalk.cyan(`\n[RoomSync] Resolving environment state in Room ${chalk.bold(e)}...`));let c=[];if(o?.api?.isAuthenticated?.())try{const t=await o.api._request("GET",`/thinknsh/${e}/cli/states`);t&&Array.isArray(t.states)&&(c=t.states)}catch(e){}const i=path.join(a,"index.json");if(fs.existsSync(i))try{const e=fs.readJsonSync(i);for(const t of e)c.some(e=>e.id===t.id)||c.push(t)}catch(e){}if(0===c.length)throw new Error(`No shared states found for room "${e}". Ask your teammate to publish one with: thinknsh room share`);const l=(t||"").toLowerCase().trim();let d=null;if(l){if(d=c.find(e=>e.id&&e.id.toLowerCase()===l||e.stateId&&e.stateId.toLowerCase()===l||e.name&&e.name.toLowerCase()===l||e.id&&e.id.toLowerCase().includes(l)||e.name&&e.name.toLowerCase().includes(l)||e.author&&e.author.toLowerCase()===l),!d)throw new Error(`State matching "${t}" not found in room ${e}. Run "thinknsh room states" to see available states.`)}else d=c[0];const h=d.id||d.stateId;console.log(chalk.cyan(` Selected state: "${chalk.bold(d.name)}" (Published by: ${chalk.bold(d.author)})`)),console.log(chalk.dim(` Size: ${d.sizeMB||d.bundleSizeMB||"0"} MB`)),console.log(chalk.dim(` Services: ${(d.services||[]).join(", ")||"Node runtime"}`));const p=path.join(r,`${h}.tar.gz.enc`),y=path.join(r,`${h}.tar.gz`);if(fs.existsSync(y)||fs.existsSync(p))console.log(chalk.dim(" Found cached local state bundle. Verifying..."));else{let t=!1;if(d.p2p&&d.p2p.localIp&&d.p2p.port&&d.p2p.token){const e=`http://${d.p2p.localIp}:${d.p2p.port}/p2p/state/${h}`;console.log(chalk.dim(` Attempting direct LAN P2P connection to teammate (${d.p2p.localIp}:${d.p2p.port})...`));try{await this._downloadStream(e,p,{"X-TNC-Sync-Token":d.p2p.token}),console.log(chalk.green(" ✓ Connected via Direct LAN P2P! Downloaded encrypted bundle at maximum speed.")),t=!0}catch(e){console.log(chalk.yellow(` Direct LAN connection unreachable (${e.message}). Switching to ThinkNCollab Cloud Relay...`))}}if(!t){if(!o?.api?.isAuthenticated?.())throw new Error("State author is not on local LAN, and you are not authenticated to request server relay. Run 'thinknsh login' first.");console.log(chalk.dim(" Requesting server-assisted in-memory stream relay..."));const n=await o.api._request("POST",`/thinknsh/${e}/cli/relay-request`,{stateId:h});if(!n?.relayUrl)throw new Error("Server could not initialize relay session: "+(n?.error||"Unknown error"));const s=`${o.api.apiUrl.replace(/\/$/,"")}${n.relayUrl}`,a=o.api.getToken();await this._downloadStream(s,p,{"x-shell-token":a}),console.log(chalk.green(" ✓ Streamed encrypted bundle via ThinkNCollab In-Memory Relay (0 customer disk bytes on server)!")),t=!0}}if(!fs.existsSync(y)){if(!fs.existsSync(p))throw new Error(`Missing encrypted bundle file: ${p}`);console.log(chalk.dim(" Decrypting AES-256-GCM bundle with Room Key & verifying authentication tag..."));try{await e2eeVault.decryptFile(p,y,e),console.log(chalk.green(" ✓ Cryptographic integrity verified! Decryption completed."))}catch(e){try{fs.removeSync(p)}catch(e){}try{fs.removeSync(y)}catch(e){}throw new Error(`SECURITY ALERT: Bundle integrity check failed or wrong room key! (${e.message})`)}}const f=path.join(a,"unpack",h);fs.ensureDirSync(f),console.log(chalk.dim(" Extracting archive with security constraints (--no-same-owner, --no-same-permissions)..."));const u=spawnSync("tar",["-xzf",y,"-C",f,"--no-same-owner","--no-same-permissions"],{stdio:"pipe"});if(0!==u.status)throw new Error(`Tar extraction failed: ${u.stderr?.toString()||"Unknown error"}`);try{this._validateAndSanitizeExtracted(f,f)}catch(e){try{fs.removeSync(f)}catch(e){}try{fs.removeSync(y)}catch(e){}throw new Error(`SECURITY VIOLATION in received snapshot: ${e.message}. Extraction terminated.`)}const m=d.services||[];if(m.includes("postgres")){const e=path.join(f,"data","postgres");if(fs.existsSync(e)){console.log(chalk.dim(" Restoring native PostgreSQL database state..."));const t=new(require("./native-service-manager"));try{t.stopPostgres()}catch(e){}const o=path.join(DATA_DIR,"postgres");if(fs.existsSync(o)){const e=path.join(DATA_DIR,`postgres-bak-${Date.now().toString(36)}`);fs.moveSync(o,e)}let n=e;!fs.existsSync(path.join(n,"PG_VERSION"))&&fs.existsSync(path.join(n,"postgres","PG_VERSION"))&&(n=path.join(n,"postgres")),fs.ensureDirSync(o),"darwin"===process.platform?spawnSync("cp",["-cR",`${n}/.`,`${o}/`],{stdio:"pipe"}):spawnSync("cp",["--reflink=auto","-a",`${n}/.`,`${o}/`],{stdio:"pipe"}),await t.startPostgres(),console.log(chalk.green(" ✓ Native PostgreSQL restored with teammate database state!"))}}if(m.includes("redis")){const e=path.join(f,"data","redis","dump.rdb");if(fs.existsSync(e)){console.log(chalk.dim(" Restoring native Redis in-memory cache state..."));const t=new(require("./native-service-manager"));try{t.stopRedis()}catch(e){}const o=path.join(DATA_DIR,"redis");fs.ensureDirSync(o),fs.copyFileSync(e,path.join(o,"dump.rdb"));try{await t.startRedis(),console.log(chalk.green(" ✓ Native Redis restored with teammate in-memory state!"))}catch(e){console.log(chalk.dim(` ✓ Saved Redis state to ${o}/dump.rdb`))}}}if(m.includes("mongodb")){const e=path.join(f,"data","mongodb");if(fs.existsSync(e)){console.log(chalk.dim(" Restoring MongoDB database state..."));try{await this._restoreMongoSnapshot(e,s)}catch(e){console.log(chalk.yellow(` [Notice] MongoDB restore skipped: ${e.message}`))}}}const g=path.join(f,"workspace");if(fs.existsSync(g)){console.log(chalk.cyan(`\n Smart Merging project workspace with ${chalk.bold(d.author)}'s state...`));const t=path.join(a,"base",d.id),r=this._mergeWorkspaceFiles(g,s,{roomId:e,author:d.author,localUser:o?.api?.getUser?.()?.name||os.userInfo().username,overwrite:n.overwrite||!1,baseDir:fs.existsSync(t)?t:null});try{const e=path.join(a,"base",d.id);fs.ensureDirSync(e),fs.copySync(g,e,{overwrite:!0})}catch(e){}r.added.length>0&&console.log(chalk.green(` ✓ ${r.added.length} new files added from teammate`)),r.cleanMerged.length>0&&console.log(chalk.green(` ✓ ${r.cleanMerged.length} files cleanly merged (zero conflicts)`)),r.updated.length>0&&console.log(chalk.cyan(` ✓ ${r.updated.length} files updated`)),r.conflicts.length>0&&(console.log(chalk.yellow.bold(`\n ⚠ [MERGE CONFLICTS DETECTED] in ${r.conflicts.length} files:`)),r.conflicts.slice(0,10).forEach(e=>{console.log(chalk.yellow(` • ${e}`))}),r.conflicts.length>10&&console.log(chalk.dim(` ... and ${r.conflicts.length-10} more`)),console.log(chalk.dim(" Standard <<<<<<< LOCAL | >>>>>>> markers inserted.")),console.log(chalk.cyan(" Open in VS Code to review and click 'Accept Change' buttons.\n"))),r.backupDir&&console.log(chalk.dim(` (Safety backup saved at ${r.backupDir}. To undo: thinknsh room undo)`))}for(const e of["package.json","thinkncollab.yml","thinkncollab.yaml"]){const t=path.join(f,e),o=path.join(s,e);fs.existsSync(t)&&!fs.existsSync(o)&&(fs.copySync(t,o),console.log(chalk.green(` ✓ Restored ${e} into workspace`)))}const S=path.join(f,".env.shared");if(fs.existsSync(S)){const e=path.join(s,".env");if(fs.existsSync(e)){const t=this._mergeEnvFile(e,S,d.author);t.addedCount>0?console.log(chalk.green(` ✓ Smart merged .env: added ${t.addedCount} new keys from teammate (${t.keys.join(", ")})!`)):console.log(chalk.dim(" ✓ .env already up-to-date with teammate configuration.")),fs.copySync(S,path.join(s,".env.room")),console.log(chalk.dim(" ✓ Full teammate environment reference saved to .env.room"))}else fs.copySync(S,e),console.log(chalk.green(" ✓ Created .env from teammate shared configuration!"))}try{fs.removeSync(f)}catch(e){}return console.log(chalk.green("\n[OK] Environment Mirrored & Merged Successfully!")),console.log(chalk.dim(` Your local environment is now in exact sync with ${d.author}.`)),console.log(chalk.dim(" Zero Docker Hub registry downloads were used.")),fs.existsSync(path.join(s,"package.json"))&&!fs.existsSync(path.join(s,"node_modules"))?console.log(chalk.cyan(' Tip: Run "npm install" to install dependencies for this project.\n')):console.log(),{success:!0,state:d}}async listStates(e,t){let o=[];if(t?.api?.isAuthenticated?.())try{const n=await t.api._request("GET",`/thinknsh/${e}/cli/states`);n&&Array.isArray(n.states)&&(o=n.states)}catch(e){}const n=this.getRoomDir(e),s=path.join(n,"index.json");if(fs.existsSync(s))try{const e=fs.readJsonSync(s);for(const t of e)o.some(e=>e.id===t.id)||o.push(t)}catch(e){}return o}}module.exports=RoomSyncManager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const https=require("https"),http=require("http"),fs=require("fs"),path=require("path"),os=require("os"),{execSync}=require("child_process"),RUNTIMES_BASE_DIR=path.join(os.homedir(),".tnc","runtimes","node"),LTS_MAP={22:"22.14.0",20:"20.18.3",18:"18.20.7",16:"16.20.2"};class RuntimeManager{static resolveVersion(t){if(!t||"string"!=typeof t)return null;const e=t.trim().replace(/^v/,"");if(/^\d+\.\d+\.\d+$/.test(e))return e;const n=e.match(/(?:>=|\^|~)?\s*(\d+)/);return n&&LTS_MAP[n[1]]?LTS_MAP[n[1]]:e.startsWith("20")?LTS_MAP[20]:e.startsWith("22")?LTS_MAP[22]:e.startsWith("18")?LTS_MAP[18]:null}static getPlatformInfo(){const t=process.platform,e=process.arch;let n=t;"darwin"===t?n="darwin":"linux"===t?n="linux":"win32"===t&&(n="win");let s=e;return"arm64"===e?s="arm64":"x64"===e?s="x64":"ia32"===e?s="x86":"arm"===e&&(s="armv7l"),{osName:n,archName:s}}static detectProjectNodeVersion(t){if(!t||!fs.existsSync(t))return null;const e=path.join(t,"thinkncollab.yml");if(fs.existsSync(e))try{const t=fs.readFileSync(e,"utf8"),n=t.match(/node:\s*["']?([^"'\n\r]+)["']?/i)||t.match(/version:\s*["']?([^"'\n\r]+)["']?/i)||t.match(/-\s*node@([0-9.]+)/i);if(n&&n[1]){const t=this.resolveVersion(n[1]);if(t)return t}}catch(t){}for(const e of[".nvmrc",".node-version"]){const n=path.join(t,e);if(fs.existsSync(n))try{const t=fs.readFileSync(n,"utf8").trim(),e=this.resolveVersion(t);if(e)return e}catch(t){}}const n=path.join(t,"package.json");if(fs.existsSync(n))try{const t=JSON.parse(fs.readFileSync(n,"utf8"));if(t.engines&&t.engines.node){const e=this.resolveVersion(t.engines.node);if(e)return e}}catch(t){}return null}static async ensureNodeRuntime(t,e=null){const n=e||this.detectProjectNodeVersion(t);if(!n)return null;const s=this.resolveVersion(n);if(!s)return null;if(process.versions.node.split(".")[0]===s.split(".")[0]&&!e)return null;const o=path.join(RUNTIMES_BASE_DIR,`v${s}`),r="win32"===process.platform?o:path.join(o,"bin"),i=path.join(r,"win32"===process.platform?"node.exe":"node");if(fs.existsSync(i))return{version:s,runtimeDir:o,binDir:r,nodePath:i};fs.mkdirSync(o,{recursive:!0});const{osName:c,archName:a}=this.getPlatformInfo(),l=`node-v${s}-${c}-${a}.${"win32"===process.platform?"zip":"tar.gz"}`,f=`https://nodejs.org/dist/v${s}/${l}`,d=path.join(os.tmpdir(),`tnc-${l}`);console.log(`[TNC Runtime] Project requires Node.js v${s} (Host has v${process.versions.node}).`),console.log(`[TNC Runtime] Downloading standalone hermetic runtime from ${f}...`),await this.downloadFile(f,d),console.log(`[TNC Runtime] Extracting Node.js v${s} to ${o}...`);try{if("win32"===process.platform){if(execSync(`tar -xf "${d}" -C "${o}" --strip-components=1 2>nul || powershell -command "Expand-Archive -Path '${d}' -DestinationPath '${o}' -Force"`),!fs.existsSync(i)){const t=path.join(o,`node-v${s}-${c}-${a}`);if(fs.existsSync(t)&&fs.statSync(t).isDirectory()){const e=fs.readdirSync(t);for(const n of e){const e=path.join(t,n),s=path.join(o,n);fs.existsSync(s)||fs.renameSync(e,s)}try{fs.rmdirSync(t)}catch(t){}}}}else execSync(`tar -xzf "${d}" -C "${o}" --strip-components=1`);fs.existsSync(i)&&fs.chmodSync(i,493);const t=path.join(r,"win32"===process.platform?"npm.cmd":"npm");fs.existsSync(t)&&fs.chmodSync(t,493)}finally{try{fs.existsSync(d)&&fs.unlinkSync(d)}catch(t){}}return fs.existsSync(i)?(console.log(`[TNC Runtime] Successfully provisioned standalone Node.js v${s} in ~/.tnc/runtimes/node/v${s}`),{version:s,runtimeDir:o,binDir:r,nodePath:i}):(console.warn(`[TNC Runtime] Failed to verify ${i}. Falling back to host Node.js.`),null)}static downloadFile(t,e){return new Promise((n,s)=>{const o=fs.createWriteStream(e),r=t=>{(t.startsWith("https")?https:http).get(t,i=>{if(i.statusCode>=300&&i.statusCode<400&&i.headers.location)return r(i.headers.location);if(200!==i.statusCode){o.close();try{fs.unlinkSync(e)}catch(t){}return s(new Error(`Failed to download ${t}: HTTP ${i.statusCode}`))}i.pipe(o),o.on("finish",()=>{o.close(()=>n(e))})}).on("error",t=>{o.close();try{fs.unlinkSync(e)}catch(t){}s(t)})};r(t)})}}module.exports=RuntimeManager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const{execSync,spawnSync}=require("child_process"),fs=require("fs-extra"),path=require("path"),os=require("os"),chalk=require("chalk"),SNAPSHOT_BASE=path.join(os.homedir(),".tnc","snapshots"),SNAPSHOT_META=path.join(SNAPSHOT_BASE,"snapshots.json");class SnapshotManager{constructor(){fs.ensureDirSync(SNAPSHOT_BASE),this.meta=this._loadMeta(),this.platform=process.platform,this.fsType=this._detectFS()}_loadMeta(){try{return fs.readJsonSync(SNAPSHOT_META)}catch{return{}}}_saveMeta(){fs.writeJsonSync(SNAPSHOT_META,this.meta,{spaces:2})}_detectFS(){if("darwin"===this.platform){try{if(execSync('diskutil info / 2>/dev/null | grep "File System"',{encoding:"utf8",stdio:"pipe"}).toLowerCase().includes("apfs"))return"apfs"}catch(e){}return"hfs+"}if("linux"===this.platform){try{const e=execSync("df -T ~/.tnc 2>/dev/null | tail -1",{encoding:"utf8",stdio:"pipe"});if(e.includes("btrfs"))return"btrfs";if(e.includes("xfs"))return"xfs";if(e.includes("ext4"))return"ext4"}catch(e){}return"ext4"}return"win32"===this.platform?"ntfs":"generic"}_getStrategy(){switch(this.fsType){case"apfs":return"apfs-cow";case"btrfs":return"btrfs-snap";case"xfs":return"reflink";case"ext4":return"reflink-auto";case"ntfs":return"robocopy";default:return"tar"}}async snapshot(e,t,s={}){const n=path.resolve(e);if(!fs.existsSync(n))throw new Error(`Source path not found: ${n}`);const a=t.replace(/[^a-zA-Z0-9_-]/g,"-"),r=`${a}-${Date.now().toString(36)}`,o=path.join(SNAPSHOT_BASE,r),i=this._getStrategy(),c=Date.now();console.log(chalk.cyan(`\n[Snapshot] Creating "${a}" using ${i}...`)),console.log(chalk.dim(` Source: ${n}`));try{switch(i){case"apfs-cow":spawnSync("cp",["-cR",n,o],{stdio:"pipe"});break;case"btrfs-snap":spawnSync("btrfs",["subvolume","snapshot",n,o],{stdio:"pipe"});break;case"reflink":fs.ensureDirSync(o),spawnSync("cp",["--reflink=always","-a",`${n}/.`,`${o}/`],{stdio:"pipe"});break;case"reflink-auto":fs.ensureDirSync(o),spawnSync("cp",["--reflink=auto","-a",`${n}/.`,`${o}/`],{stdio:"pipe"});break;case"robocopy":fs.ensureDirSync(o),spawnSync("robocopy",[n,o,"/E","/DCOPY:T"],{stdio:"pipe"});break;default:spawnSync("tar",["-czf",`${o}.tar.gz`,"-C",path.dirname(n),path.basename(n)],{stdio:"pipe"})}const e=Date.now()-c,t=this._dirSize("tar"===i?void 0:o),p=["apfs-cow","btrfs-snap","reflink"].includes(i),f={id:r,name:a,source:n,snapDir:"tar"===i?`${o}.tar.gz`:o,strategy:i,fsType:this.fsType,isCoW:p,sizeMB:t,createdAt:(new Date).toISOString(),elapsedMs:e,tags:s.tags||{},description:s.description||""};return this.meta[r]=f,this._saveMeta(),console.log(chalk.green(`[OK] Snapshot "${a}" created in ${e}ms`)),p?console.log(chalk.dim(" CoW: Hardware/Filesystem instant clone active (zero extra disk until modified) ✓")):console.log(chalk.yellow(` Notice: ${this.fsType} does not support instant CoW reflinks; standard copy performed.`)),console.log(chalk.dim(` ID: ${r}`)),f}catch(e){throw new Error(`Snapshot failed (${i}): ${e.message}`)}}async snapshotSafe(e,t,s=null,n=null,a={}){let r=!1;if(s&&n){const e=s.loadMeta(n);e&&"RUNNING"===e.state&&(r=!0,console.log(chalk.yellow(`[SnapshotSafe] Flushing & pausing container "${n}" for crash-free snapshot consistency...`)),await s.freeze(n))}try{return await this.snapshot(e,t,a)}finally{r&&s&&n&&(console.log(chalk.cyan(`[SnapshotSafe] Resuming container "${n}"...`)),await s.resume(n))}}async restore(e,t,s={}){const n=this.meta[e];if(!n)throw new Error(`Snapshot "${e}" not found.`);const a=path.resolve(t||n.source);return console.log(chalk.cyan(`\n[Snapshot] Restoring "${n.name}" → ${a}...`)),s.backup&&await this.snapshot(a,`pre-restore-${n.name}`),"tar"===n.strategy?(fs.ensureDirSync(a),spawnSync("tar",["-xzf",n.snapDir,"-C",path.dirname(a)],{stdio:"pipe"})):"btrfs-snap"===n.strategy?(fs.existsSync(a)&&spawnSync("btrfs",["subvolume","delete",a],{stdio:"pipe"}),spawnSync("btrfs",["subvolume","snapshot",n.snapDir,a],{stdio:"pipe"})):(fs.existsSync(a)&&fs.removeSync(a),fs.ensureDirSync(a),"apfs-cow"===n.strategy?spawnSync("cp",["-cR",n.snapDir,a],{stdio:"pipe"}):spawnSync("cp",["--reflink=auto","-a",`${n.snapDir}/.`,`${a}/`],{stdio:"pipe"})),console.log(chalk.green(`[OK] Restored to ${a}`)),{success:!0,target:a}}diff(e,t){const s=this.meta[e],n=this.meta[t];if(!s||!n)throw new Error("One or both snapshots not found.");if("tar"===s.strategy||"tar"===n.strategy)return{method:"tar",note:"Diff not available for tar snapshots"};try{const e=spawnSync("diff",["-rq","--brief",s.snapDir,n.snapDir],{encoding:"utf8",stdio:"pipe"}).stdout||"",t=e.trim().split("\n").filter(Boolean).length;return{method:"diff",snapshot1:s.name,snapshot2:n.name,changedFiles:t,details:e.trim().split("\n").filter(Boolean).slice(0,50)}}catch{return{method:"diff",error:"diff command failed"}}}delete(e){const t=this.meta[e];if(!t)throw new Error(`Snapshot "${e}" not found.`);if("btrfs-snap"===t.strategy)try{execSync(`btrfs subvolume delete "${t.snapDir}"`,{stdio:"pipe"})}catch(e){fs.removeSync(t.snapDir)}else fs.existsSync(t.snapDir)&&fs.removeSync(t.snapDir);return delete this.meta[e],this._saveMeta(),{success:!0,deleted:e}}list(e={}){let t=Object.values(this.meta);return e.name&&(t=t.filter(t=>t.name.includes(e.name))),t.sort((e,t)=>new Date(t.createdAt)-new Date(e.createdAt))}inspect(e){return this.meta[e]||null}prune(e=3){const t={};for(const e of Object.values(this.meta))t[e.name]||(t[e.name]=[]),t[e.name].push(e);let s=0;for(const[n,a]of Object.entries(t)){const t=a.sort((e,t)=>new Date(t.createdAt)-new Date(e.createdAt)).slice(e);for(const e of t)try{this.delete(e.id),s++}catch(e){}}return{pruned:s}}_dirSize(e){if(!e||!fs.existsSync(e))return 0;try{const t=spawnSync("du",["-sm",e],{encoding:"utf8",stdio:"pipe"});return parseInt((t.stdout||"").split(/\s+/)[0],10)||0}catch{return 0}}systemInfo(){return{platform:this.platform,fsType:this.fsType,strategy:this._getStrategy(),isCoW:["apfs","btrfs","xfs"].includes(this.fsType),snapshotBase:SNAPSHOT_BASE,totalSnapshots:Object.keys(this.meta).length}}}module.exports=SnapshotManager;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ let fs;try{fs=require("fs-extra")}catch(r){try{fs=require("/Users/ramansingh/ThinkNCollab-Shell-V2/node_modules/fs-extra")}catch(r){const e=require("fs");fs={...e,ensureDirSync:r=>e.mkdirSync(r,{recursive:!0}),readJsonSync:r=>JSON.parse(e.readFileSync(r,"utf8")),writeJsonSync:(r,i)=>e.writeFileSync(r,JSON.stringify(i,null,2),"utf8"),removeSync:r=>e.rmSync(r,{recursive:!0,force:!0})}}}const path=require("path"),{execSync}=require("child_process");class SandboxStorage{constructor(r,e={}){this.id=r,this.rootDir=e.rootDir||path.join(process.env.HOME||"/tmp",".tnc","sandboxes",r),this.upperDir=path.join(this.rootDir,"diff"),this.workDir=path.join(this.rootDir,"work"),this.mergedDir=path.join(this.rootDir,"merged"),this.isLinux="linux"===process.platform}initStorage(r,e=null,i=null){if(fs.ensureDirSync(this.rootDir),fs.ensureDirSync(this.upperDir),fs.ensureDirSync(this.workDir),fs.ensureDirSync(this.mergedDir),this.isLinux&&r&&fs.existsSync(r))try{execSync(`mount -t overlay overlay -o lowerdir=${r},upperdir=${this.upperDir},workdir=${this.workDir} ${this.mergedDir}`)}catch(r){console.warn(`[TNC Storage] OverlayFS mount warning: ${r.message}`)}if(e&&fs.existsSync(e)){const r=path.join(this.mergedDir,"workspace");if(fs.ensureDirSync(r),this.isLinux)try{execSync(`mount --bind "${e}" "${r}"`)}catch(i){try{fs.existsSync(r)||fs.symlinkSync(e,r)}catch(r){}}else try{fs.existsSync(r)&&0===fs.readdirSync(r).length&&fs.removeSync(r),fs.existsSync(r)||fs.symlinkSync(e,r)}catch(r){}}if(i&&fs.existsSync(i)){const r=path.join(this.mergedDir,"opt","node");if(fs.ensureDirSync(r),this.isLinux)try{execSync(`mount --bind "${i}" "${r}"`)}catch(e){try{fs.existsSync(r)||fs.symlinkSync(i,r)}catch(r){}}else try{fs.existsSync(r)||fs.symlinkSync(i,r)}catch(r){}}return{rootDir:this.rootDir,mergedDir:this.mergedDir,upperDir:this.upperDir}}unmountStorage(){if(this.isLinux){const r=path.join(this.mergedDir,"opt","node");try{execSync(`umount -l "${r}"`,{stdio:"ignore"})}catch(r){}const e=path.join(this.mergedDir,"workspace");try{execSync(`umount -l "${e}"`,{stdio:"ignore"})}catch(r){}try{execSync(`umount -l "${this.mergedDir}"`,{stdio:"ignore"})}catch(r){}}}purge(){this.unmountStorage();try{return fs.removeSync(this.rootDir),!0}catch{return!1}}}module.exports=SandboxStorage;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const http=require("http"),path=require("path"),fs=require("fs-extra"),{execSync}=require("child_process");class SandboxSupervisor{constructor(t){this.engine=t,this.watched=new Map,this.checkInterval=null}startSupervision(t=3e3){this.checkInterval||(this.checkInterval=setInterval(()=>this.runSupervisionCycle(),t))}stopSupervision(){this.checkInterval&&(clearInterval(this.checkInterval),this.checkInterval=null)}watch(t,e){this.watched.set(t,{meta:e,restartCount:e.restartCount||0,lastCrash:null,lastStartTime:Date.now(),rapidCrashCount:0,health:"HEALTHY"}),this.startSupervision()}unwatch(t){this.watched.delete(t),0===this.watched.size&&this.stopSupervision()}async runSupervisionCycle(){for(const[t,e]of this.watched.entries()){const s=this.engine.loadMeta(t);if(!s){this.watched.delete(t);continue}if("FROZEN"===s.state||"DESTROYED"===s.state)continue;let a=!1;if(s.pid)try{process.kill(s.pid,0),a=!0}catch{a=!1}if(a||"RUNNING"!==s.state){if(a){const a=this.gatherTelemetry(s.pid,s.cwd);s.cpuPercent=a.cpu,s.memoryMB=a.memory,a.addons&&a.addons.length>0&&(s.addons=a.addons),a.sfu&&(s.sfu=a.sfu);const n=Date.now();if(!e.lastHealthProbe||n-e.lastHealthProbe>=15e3)if(e.lastHealthProbe=n,s.hostPort){const t=await this.probeHttp(s.hostPort,s.healthPath||"/health");s.health=t?"HEALTHY":"DEGRADED"}else s.health="HEALTHY";this.engine.saveMeta(t,s)}}else await this.handleCrash(t,s,e)}}async handleCrash(t,e,s){const a=Date.now();s.restartCount=(s.restartCount||0)+1,s.lastCrash=(new Date).toISOString(),e.restartCount=s.restartCount,e.state="CRASH_RECOVERING",e.health="STARTING";const n=path.join(this.engine.baseDir,t,"crash.log"),r=`[${s.lastCrash}] CRASH DETECTED on PID ${e.pid}. Auto-healing trigger (#${s.restartCount})\n`;try{fs.appendFileSync(n,r,"utf8")}catch(t){}const o=s.lastStartTime&&a-s.lastStartTime<4e3;if(s.rapidCrashCount=o?(s.rapidCrashCount||0)+1:1,s.rapidCrashCount>=3||s.restartCount>5)return e.state="DEAD",e.health="DEAD",this.engine.saveMeta(t,e),this.unwatch(t),console.log(`\n[TNC:AutoHeal] Container "${t}" terminated after repeated fatal crashes. Process halted.`),void console.log(`Check logs with: thinknsh logs -f ${t}`);console.log(`\n[TNC:AutoHeal] Container "${t}" crashed! Reviving process (Restart #${s.restartCount})...`);try{s.lastStartTime=Date.now();const a=await this.engine.start(t,{cmd:e.cmd,cwd:e.cwd,hostPort:e.hostPort,containerPort:e.containerPort,memoryLimit:e.memoryLimit});a.success&&(e.pid=a.meta.pid,e.state="RUNNING",e.health="HEALTHY",this.engine.saveMeta(t,e),console.log(`[TNC:AutoHeal] Container "${t}" successfully revived on PID ${e.pid}!`))}catch(s){e.state="DEAD",e.health="DEAD",this.engine.saveMeta(t,e),this.unwatch(t)}}probeHttp(t,e="/health"){return new Promise(s=>{const a=http.request({hostname:"127.0.0.1",port:t,path:e,method:"GET",headers:{"User-Agent":"TNC-Supervisor/1.0"},timeout:1e3},a=>{404!==a.statusCode||"/health"!==e?s(a.statusCode<500):this.probeHttp(t,"/").then(s)});a.on("error",()=>s(!1)),a.on("timeout",()=>{a.destroy(),s(!1)}),a.end()})}gatherTelemetry(t,e=null){if(!t)return{cpu:0,memory:0,sfu:null,addons:[]};try{let s=[t];try{const e=execSync(`pgrep -P ${t} 2>/dev/null || echo ""`,{encoding:"utf8"}).trim();if(e){const t=e.split(/\s+/).filter(Boolean);s.push(...t);for(const e of t)try{const t=execSync(`pgrep -P ${e} 2>/dev/null || echo ""`,{encoding:"utf8"}).trim();t&&s.push(...t.split(/\s+/).filter(Boolean))}catch(t){}}}catch(t){}s=Array.from(new Set(s.map(Number).filter(Boolean)));let a=0,n=0,r=null;const o=new Map;for(const e of s)try{const s=execSync(`ps -p ${e} -o comm=,%cpu=,rss= 2>/dev/null || echo ""`,{encoding:"utf8"}).trim();if(s){const i=s.split(/\s+/).filter(Boolean),c=i[0]||"",h=parseFloat(i[1])||0,l=parseInt(i[2],10)||0;if(a+=h,n+=l,c.includes("mediasoup-worker")||s.includes("mediasoup"))r={active:!0,workerPid:e,ports:"10000-10100"},o.set("sfu",{id:"sfu",name:"SFU Media (mediasoup)",icon:"fas fa-video",pid:e,status:"active",ports:"10000-10100/udp"});else if(c.includes("redis-server")||s.includes("redis"))o.set("redis",{id:"redis",name:"Redis Broker",icon:"fas fa-database",pid:e,status:"active",ports:"6379"});else if(!(e===t||c.includes("npm")||c.includes("sh")||c.includes("bash")||c.includes("entrypoint"))){const t=path.basename(c);o.set(`worker-${e}`,{id:`worker-${e}`,name:`${t} (Worker)`,icon:t.includes("python")?"fab fa-python":"fas fa-microchip",pid:e,status:"active"})}}}catch(t){}if(e)try{const t=path.join(e,"thinkncollab.yml");if(fs.existsSync(t)){const e=fs.readFileSync(t,"utf8");if(e.includes("addons:")){const t=e.split("\n");let s=!1;for(const e of t){const t=e.trim();if("addons:"!==t){if(s&&t.endsWith(":")&&!t.startsWith("-")&&(s=!1),s&&t.startsWith("-")){const e=t.replace(/^-\s*/,"").replace(/["']/g,"");o.has(e)||o.set(e,{id:e,name:"sfu"===e?"SFU Media (mediasoup)":"redis"===e?"Redis Broker":e,icon:"sfu"===e?"fas fa-video":"redis"===e?"fas fa-database":"fas fa-puzzle-piece",status:"active"})}}else s=!0}}}}catch(t){}const i=Math.round(n/1024);return{cpu:Math.round(10*a)/10,memory:i,sfu:r,addons:Array.from(o.values())}}catch{return{cpu:0,memory:0,sfu:null,addons:[]}}}}module.exports=SandboxSupervisor;
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const path=require("path"),fs=require("fs-extra"),os=require("os"),SandboxTunnel=require("./tunnel"),sandboxId=process.argv[2],localPort=parseInt(process.argv[3],10),targetServer=process.argv[4]||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com",userId=process.argv[5]||process.env.THINKNCOLLAB_USER_ID||null;sandboxId&&localPort||(console.error("Usage: node tunnel-daemon.js <sandboxId> <localPort> [serverUrl] [userId]"),process.exit(1));const sandboxDir=path.join(os.homedir(),".tnc","sandboxes",sandboxId);fs.ensureDirSync(sandboxDir);const pidFile=path.join(sandboxDir,"tunnel.pid");fs.writeFileSync(pidFile,String(process.pid));const tunnel=new SandboxTunnel,standaloneShellContext={ws:{config:{serverUrl:targetServer},getCurrentRoom:()=>null},api:{apiUrl:targetServer,getUser:()=>({_id:userId}),userId},config:{serverUrl:targetServer,userId}};function cleanup(){try{fs.existsSync(pidFile)&&fs.removeSync(pidFile)}catch(e){}tunnel.stop(),process.exit(0)}tunnel.connect(sandboxId,localPort,standaloneShellContext),process.on("SIGINT",cleanup),process.on("SIGTERM",cleanup),process.on("SIGHUP",cleanup),process.on("exit",()=>{try{fs.existsSync(pidFile)&&fs.removeSync(pidFile)}catch(e){}});
@@ -0,0 +1,7 @@
1
+ /**
2
+ * @license ThinkNCollab Enterprise & Open Core Ecosystem
3
+ * Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
4
+ * PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
5
+ * UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
6
+ */
7
+ const http=require("http");class SandboxTunnel{constructor(){this.socket=null,this.sandboxId=null,this.localPort=null,this.active=!1,this._heartbeatInterval=null,this._reconnectTimer=null,this._stopping=!1,this._targetServer=null,this._userId=null,this._shellToken=null}connect(t,e,s){this.sandboxId=t,this.localPort=e,this._stopping=!1,this._targetServer=(s?.ws?.config?.serverUrl||s?.api?.apiUrl||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com").replace(/\/$/,""),this._userId=s?.api?.getUser?.()?._id||s?.api?.getUser?.()?.id||s?.api?.userId||s?.config?.user?._id||process.env.THINKNCOLLAB_USER_ID||null,this._shellToken=s?.config?.shellToken||s?.api?.shellToken||process.env.THINKNCOLLAB_TOKEN||null,this._doConnect()}_doConnect(){if(this._stopping)return;let t;try{t=require("socket.io-client")}catch(e){try{t=require("/Users/ramansingh/ThinkNCollab-Shell-V2/node_modules/socket.io-client")}catch(t){return}}const e=t(this._targetServer+"/thinknsh",{transports:["websocket","polling"],timeout:5e3,reconnection:!1,auth:{userId:this._userId,shellToken:this._shellToken}});this.socket=e,e.on("connect",()=>{this.active=!0,e.emit("sandbox:tunnel-register",{sandboxId:this.sandboxId,localPort:this.localPort,userId:this._userId}),this._startHeartbeat()}),e.on("tunnel:request",t=>{this._handleRequest(t,e)}),e.on("disconnect",()=>{this.active=!1,this._stopHeartbeat(),this._stopping||(this._reconnectTimer=setTimeout(()=>this._doConnect(),3e3))}),e.on("connect_error",()=>{this.active=!1,this._stopHeartbeat(),this._stopping||(this._reconnectTimer=setTimeout(()=>this._doConnect(),5e3))})}_handleRequest(t,e){if(!t||!t.requestId)return;const{requestId:s,method:n,path:o,headers:r,body:i}=t,a=this.localPort,h={...r||{}};delete h.host,delete h.connection,delete h["transfer-encoding"],h.host=`127.0.0.1:${a}`;const l=i?Buffer.from(i,"base64"):null;l&&l.length>0&&(h["content-length"]=l.length);const c={hostname:"127.0.0.1",port:a,path:o||"/",method:n||"GET",headers:h,timeout:3e4},d=http.request(c,t=>{const n=[];t.on("data",t=>n.push(t)),t.on("end",()=>{const o=Buffer.concat(n);e.emit("tunnel:response",{requestId:s,statusCode:t.statusCode,headers:t.headers,body:o.toString("base64")})})});d.on("error",t=>{e.emit("tunnel:response",{requestId:s,statusCode:502,headers:{"content-type":"text/plain"},body:Buffer.from(`Tunnel Error: ${t.message}`).toString("base64")})}),d.on("timeout",()=>{d.destroy(),e.emit("tunnel:response",{requestId:s,statusCode:504,headers:{"content-type":"text/plain"},body:Buffer.from("Tunnel Gateway Timeout").toString("base64")})}),l&&l.length>0&&d.write(l),d.end()}_startHeartbeat(){this._stopHeartbeat(),this._heartbeatInterval=setInterval(()=>{this.socket&&this.active&&this.socket.emit("sandbox:tunnel-ping",{sandboxId:this.sandboxId})},15e3)}_stopHeartbeat(){this._heartbeatInterval&&(clearInterval(this._heartbeatInterval),this._heartbeatInterval=null)}stop(){if(this._stopping=!0,this.active=!1,this._stopHeartbeat(),this._reconnectTimer&&clearTimeout(this._reconnectTimer),this.socket){try{this.socket.disconnect()}catch(t){}this.socket=null}}}module.exports=SandboxTunnel;