thinkncollab-shell 0.0.20 → 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.
- package/LICENSE +82 -0
- package/Readme.md +246 -502
- package/dist/bin/thinknsh.js +8 -0
- package/dist/src/commands/auth/login.js +7 -0
- package/dist/src/commands/auth/logout.js +7 -0
- package/dist/src/commands/auth/register.js +7 -0
- package/dist/src/commands/auth/whoami.js +7 -0
- package/dist/src/commands/dock/bench.js +7 -0
- package/dist/src/commands/dock/ci.js +7 -0
- package/dist/src/commands/dock/cluster.js +7 -0
- package/dist/src/commands/dock/containers.js +7 -0
- package/dist/src/commands/dock/dock.js +7 -0
- package/dist/src/commands/github/commits.js +7 -0
- package/dist/src/commands/github/issue-create.js +7 -0
- package/dist/src/commands/github/issues.js +7 -0
- package/dist/src/commands/github/pulls.js +7 -0
- package/dist/src/commands/github/statusgh.js +7 -0
- package/dist/src/commands/index.js +7 -0
- package/dist/src/commands/message/say.js +7 -0
- package/dist/src/commands/room/join.js +7 -0
- package/dist/src/commands/room/leave.js +7 -0
- package/dist/src/commands/room/rooms.js +7 -0
- package/dist/src/commands/room/sync.js +7 -0
- package/dist/src/commands/sandbox/console.js +7 -0
- package/dist/src/commands/sandbox/dep.js +7 -0
- package/dist/src/commands/sandbox/deploy.js +7 -0
- package/dist/src/commands/sandbox/dev.js +7 -0
- package/dist/src/commands/sandbox/exec.js +7 -0
- package/dist/src/commands/sandbox/freeze.js +7 -0
- package/dist/src/commands/sandbox/push.js +7 -0
- package/dist/src/commands/sandbox/resume.js +7 -0
- package/dist/src/commands/sandbox/sandbox.js +7 -0
- package/dist/src/commands/sandbox/share.js +7 -0
- package/dist/src/commands/sandbox/snapshot.js +7 -0
- package/dist/src/commands/sandbox/sync-deps.js +7 -0
- package/dist/src/commands/shell/doctor.js +7 -0
- package/dist/src/commands/shell/notifications.js +7 -0
- package/dist/src/commands/shell/notify.js +7 -0
- package/dist/src/commands/shell/status.js +7 -0
- package/dist/src/commands/shell/version.js +7 -0
- package/dist/src/commands/tasks/build.js +7 -0
- package/dist/src/commands/tasks/complete.js +7 -0
- package/dist/src/commands/tasks/create-task.js +7 -0
- package/dist/src/commands/tasks/loadtest.js +7 -0
- package/dist/src/commands/tasks/task-status.js +7 -0
- package/dist/src/commands/tasks/task.js +7 -0
- package/dist/src/commands/tasks/tasks.js +7 -0
- package/dist/src/commands/team/invite.js +7 -0
- package/dist/src/commands/team/myteam.js +7 -0
- package/dist/src/commands/team/teammates.js +7 -0
- package/dist/src/commands/terminal/share.js +7 -0
- package/dist/src/core/api-client.js +7 -0
- package/dist/src/core/crypto/e2ee-vault.js +7 -0
- package/dist/src/core/dep-store.js +7 -0
- package/dist/src/core/dependency-tracker.js +7 -0
- package/dist/src/core/geekload.js +7 -0
- package/dist/src/core/local-runner.js +7 -0
- package/dist/src/core/multi-lang-scanner.js +7 -0
- package/dist/src/core/notification-window.js +8 -0
- package/dist/src/core/notify-auth.js +7 -0
- package/dist/src/core/project-config.js +7 -0
- package/dist/src/core/sandbox/arch-shim.js +7 -0
- package/dist/src/core/sandbox/capsule-builder.js +7 -0
- package/dist/src/core/sandbox/cgroups.js +7 -0
- package/dist/src/core/sandbox/container-monitor.js +7 -0
- package/dist/src/core/sandbox/crun-manager.js +7 -0
- package/dist/src/core/sandbox/engine.js +7 -0
- package/dist/src/core/sandbox/image-manager.js +7 -0
- package/dist/src/core/sandbox/image-store.js +7 -0
- package/dist/src/core/sandbox/index.js +7 -0
- package/dist/src/core/sandbox/macos-sandbox.js +7 -0
- package/dist/src/core/sandbox/microvm-manager.js +7 -0
- package/dist/src/core/sandbox/native-service-manager.js +7 -0
- package/dist/src/core/sandbox/network.js +7 -0
- package/dist/src/core/sandbox/oci-exporter.js +7 -0
- package/dist/src/core/sandbox/oci-puller.js +7 -0
- package/dist/src/core/sandbox/oci-runtime-spec.js +7 -0
- package/dist/src/core/sandbox/policy-engine.js +7 -0
- package/dist/src/core/sandbox/port-proxy-daemon.js +7 -0
- package/dist/src/core/sandbox/room-sync-manager.js +7 -0
- package/dist/src/core/sandbox/runtime-manager.js +7 -0
- package/dist/src/core/sandbox/snapshot-manager.js +7 -0
- package/dist/src/core/sandbox/storage.js +7 -0
- package/dist/src/core/sandbox/supervisor.js +7 -0
- package/dist/src/core/sandbox/tunnel-daemon.js +7 -0
- package/dist/src/core/sandbox/tunnel.js +7 -0
- package/dist/src/core/sandbox/window-launcher.js +7 -0
- package/dist/src/core/sandbox/windows-wsl2.js +7 -0
- package/dist/src/core/sandbox-window.js +8 -0
- package/dist/src/core/shell.js +7 -0
- package/dist/src/core/websocket.js +7 -0
- package/dist/src/dock/api/routes.js +7 -0
- package/dist/src/dock/api/server.js +7 -0
- package/dist/src/dock/api/wire.js +7 -0
- package/dist/src/dock/bench/memory.js +7 -0
- package/dist/src/dock/bench/runner.js +7 -0
- package/dist/src/dock/build/builder.js +7 -0
- package/dist/src/dock/build/buildfile.js +7 -0
- package/dist/src/dock/build/buildkit.js +7 -0
- package/dist/src/dock/build/changes.js +7 -0
- package/dist/src/dock/ci/actions.js +7 -0
- package/dist/src/dock/ci/runner.js +7 -0
- package/dist/src/dock/ci/workflow.js +7 -0
- package/dist/src/dock/cli/api.js +7 -0
- package/dist/src/dock/cli/args.js +7 -0
- package/dist/src/dock/cli/commands.js +7 -0
- package/dist/src/dock/cli/compose.js +7 -0
- package/dist/src/dock/cli/context.js +7 -0
- package/dist/src/dock/cli/files.js +7 -0
- package/dist/src/dock/cli/format.js +7 -0
- package/dist/src/dock/cli/manage.js +7 -0
- package/dist/src/dock/cli/moved.js +7 -0
- package/dist/src/dock/cli/services.js +7 -0
- package/dist/src/dock/cli/session.js +7 -0
- package/dist/src/dock/cli/workspace.js +7 -0
- package/dist/src/dock/client.js +7 -0
- package/dist/src/dock/compat/cases.js +7 -0
- package/dist/src/dock/compat/runner.js +7 -0
- package/dist/src/dock/config.js +7 -0
- package/dist/src/dock/daemon-main.js +7 -0
- package/dist/src/dock/daemon.js +7 -0
- package/dist/src/dock/deploy/compose.js +7 -0
- package/dist/src/dock/engine.js +7 -0
- package/dist/src/dock/image/archive.js +7 -0
- package/dist/src/dock/image/auth.js +7 -0
- package/dist/src/dock/image/reference.js +7 -0
- package/dist/src/dock/image/registry.js +7 -0
- package/dist/src/dock/image/tar.js +7 -0
- package/dist/src/dock/index.js +7 -0
- package/dist/src/dock/linux/host.js +7 -0
- package/dist/src/dock/paths.js +7 -0
- package/dist/src/dock/runtime/emulation.js +7 -0
- package/dist/src/dock/runtime/fsevents.js +7 -0
- package/dist/src/dock/runtime/fsops.js +7 -0
- package/dist/src/dock/runtime/hostfwd.js +7 -0
- package/dist/src/dock/runtime/logs.js +7 -0
- package/dist/src/dock/runtime/network.js +7 -0
- package/dist/src/dock/runtime/ports.js +7 -0
- package/dist/src/dock/runtime/rootfs.js +7 -0
- package/dist/src/dock/runtime/seccomp-profile.LICENSE +202 -0
- package/dist/src/dock/runtime/seccomp-profile.json +1227 -0
- package/dist/src/dock/runtime/seccomp.js +7 -0
- package/dist/src/dock/runtime/spec.js +7 -0
- package/dist/src/dock/runtime/stats.js +7 -0
- package/dist/src/dock/runtime/volumes.js +7 -0
- package/dist/src/dock/selftest.js +7 -0
- package/dist/src/dock/setup/app.js +7 -0
- package/dist/src/dock/setup/autostart.js +7 -0
- package/dist/src/dock/setup/index.js +7 -0
- package/dist/src/dock/setup/integrate.js +7 -0
- package/dist/src/dock/setup/migrate.js +7 -0
- package/dist/src/dock/state/store.js +7 -0
- package/dist/src/dock/ui/page.js +385 -0
- package/dist/src/dock/ui/server.js +7 -0
- package/dist/src/dock/version.js +7 -0
- package/dist/src/dock/workspace/apps.js +7 -0
- package/dist/src/dock/workspace/workspace.js +7 -0
- package/dist/src/events/notifications.js +7 -0
- package/dist/src/utils/logger.js +7 -0
- package/dist/src/utils/terminalCrypto.js +7 -0
- package/package.json +41 -16
- package/bin/thinknsh.js +0 -71
- package/src/commands/auth/login.js +0 -84
- package/src/commands/auth/logout.js +0 -32
- package/src/commands/auth/register.js +0 -52
- package/src/commands/auth/whoami.js +0 -52
- package/src/commands/github/commits.js +0 -58
- package/src/commands/github/issue-create.js +0 -59
- package/src/commands/github/issues.js +0 -57
- package/src/commands/github/pulls.js +0 -58
- package/src/commands/github/statusgh.js +0 -50
- package/src/commands/index.js +0 -136
- package/src/commands/message/say.js +0 -49
- package/src/commands/room/join.js +0 -142
- package/src/commands/room/leave.js +0 -24
- package/src/commands/room/rooms.js +0 -66
- package/src/commands/shell/notify.js +0 -88
- package/src/commands/shell/status.js +0 -73
- package/src/commands/shell/version.js +0 -18
- package/src/commands/tasks/build.js +0 -190
- package/src/commands/tasks/complete.js +0 -940
- package/src/commands/tasks/create-task.js +0 -101
- package/src/commands/tasks/loadtest.js +0 -101
- package/src/commands/tasks/suggestTask.js +0 -0
- package/src/commands/tasks/task-status.js +0 -72
- package/src/commands/tasks/task.js +0 -182
- package/src/commands/tasks/tasks.js +0 -184
- package/src/commands/team/invite.js +0 -46
- package/src/commands/team/myteam.js +0 -78
- package/src/commands/team/teammates.js +0 -52
- package/src/commands/terminal/share.js +0 -261
- package/src/core/api-client.js +0 -246
- package/src/core/geekload.js +0 -524
- package/src/core/local-runner.js +0 -861
- package/src/core/notification-window.js +0 -165
- package/src/core/project-config.js +0 -100
- package/src/core/shell.js +0 -734
- package/src/core/websocket.js +0 -657
- package/src/events/notifications.js +0 -184
- package/src/utils/logger.js +0 -159
- 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 path=require("path");module.exports={name:"notifications",description:"Stream live room notifications & chat in current terminal tab (VS Code split view)",aliases:["panel","notif-panel","alerts"],requiresAuth:!1,execute:async(e,i)=>(i?.notifyPort&&(process.env.THINKNSH_NOTIFY_PORT=String(i.notifyPort)),require("../../core/notification-window"),new Promise(()=>{}))};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"notify",description:"Send a notification to the current room",aliases:["notif"],requiresAuth:!1,requiresRoom:!1,async execute(o,e){if(o[0]&&/^\d{4,5}$/.test(o[0])){const e=parseInt(o[0],10);return process.env.THINKNSH_NOTIFY_PORT=String(e),process.argv[2]=String(e),require("../../core/notification-window"),new Promise(()=>{})}if(!o.length)return e?.notifyPort&&(console.log(chalk.cyan(`Live notification server active on port: ${chalk.bold(e.notifyPort)}`)),console.log(chalk.dim(`To view notifications in another tab/terminal: thinknsh notify ${e.notifyPort}\n`))),console.log(chalk.yellow("Usage:")),console.log(chalk.white(" thinknsh notify <port> - Open live notification panel")),void console.log(chalk.white(" notify <level> <message> - Send room notification (info/warn/error/success)"));if(!e?.api?.isAuthenticated())return void console.log(chalk.red("[FAIL] Please login first: login"));if(!e?.ws?.getCurrentRoom())return void console.log(chalk.red("[FAIL] Please join a room first: join <room-id>"));let n,i;if(["info","warn","warning","error","success"].includes(o[0].toLowerCase())?(n="warning"===o[0].toLowerCase()?"warn":o[0].toLowerCase(),i=o.slice(1).join(" ")):(n="info",i=o.join(" ")),!i.trim())return void console.log(chalk.red("[FAIL] Message cannot be empty"));const t={info:chalk.blue,warn:chalk.yellow,error:chalk.red,success:chalk.green}[n]||chalk.blue,r={info:"ℹ️ ",warn:"[WARN] ",error:"[FAIL]",success:"[OK]"}[n]||"ℹ️ ";try{await e.ws.sendNotification({type:n,title:`${n.charAt(0).toUpperCase()+n.slice(1)} from terminal`,message:i,data:{sentBy:e.api.getUser()?.name||"unknown",via:"thinknsh"}},"room"),e.pushNotification({type:"notification",level:n,title:`Notify — ${n}`,message:i}),console.log(t(`${r} Notification sent → [${n}] ${i}`))}catch(o){console.log(chalk.red(`[FAIL] Failed to send notification: ${o.message}`))}}};
|
|
@@ -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 chalk=require("chalk"),os=require("os");module.exports={name:"status",description:"Show shell and connection status",aliases:["st"],requiresAuth:!1,async execute(o,l){const e=l.ws.getUser(),s=l.ws.getCurrentRoom(),a=l.ws.isConnected();console.log(chalk.cyan("\n ThinkNCollab Shell Status")),console.log(chalk.dim("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━")),console.log(` ${chalk.yellow("Connection:")} ${a?chalk.green("● Online"):chalk.red("○ Offline")}`),a&&(console.log(` ${chalk.yellow("Server:")} ${l.ws.config.wsUrl}`),console.log(` ${chalk.yellow("Socket ID:")} ${l.ws.getSocketId()||"N/A"}`)),console.log(` ${chalk.yellow("Auth:")} ${e?chalk.green(`[OK] ${e.email}`):chalk.red("[FAIL] Not logged in")}`),e&&(console.log(` ${chalk.yellow("User:")} ${e.name||e.email}`),e.team&&console.log(` ${chalk.yellow("Team:")} ${e.team}`)),s?(console.log(` ${chalk.yellow("Current Room:")} ${chalk.cyan(s.name)} (${s.id})`),console.log(` ${chalk.yellow("Participants:")} ${s.participants?.length||0}`)):console.log(` ${chalk.yellow("Current Room:")} ${chalk.gray("None")}`),console.log(`\n ${chalk.yellow("Shell Version:")} ${require("../../../package.json").version}`),console.log(` ${chalk.yellow("Node Version:")} ${process.version}`),console.log(` ${chalk.yellow("Platform:")} ${os.platform()} ${os.arch()}`),console.log(` ${chalk.yellow("Uptime:")} ${Math.floor(process.uptime()/60)}m`),console.log(` ${chalk.yellow("Memory:")} ${Math.round(process.memoryUsage().rss/1024/1024)} MB`),console.log(` ${chalk.yellow("History:")} ${l.history.length} commands`),console.log(chalk.dim("\n Tips:")),e?s?(console.log(chalk.dim(" • Send messages with: say <message>")),console.log(chalk.dim(" • See teammates: teammates")),console.log(chalk.dim(" • Leave room: leave"))):(console.log(chalk.dim(" • Join a room with: join <room-id>")),console.log(chalk.dim(" • See available rooms: rooms"))):console.log(chalk.dim(" • Login with: login <email> <password>")),console.log()}};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"version",description:"Show shell CLI version",aliases:["v","-v","--version"],requiresAuth:!1,async execute(e,o){const n=require("../../../package.json");console.log(`\n ${chalk.yellow("ThinkNCollab CLI Version:")} ${chalk.green(n.version)}\n`)}};
|
|
@@ -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 chalk=require("chalk"),path=require("path"),fs=require("fs-extra"),{spawn}=require("child_process"),CapsuleBuilder=require("../../core/sandbox/capsule-builder"),{BUILD_FILE}=require("../../dock/api/wire");module.exports={name:"pipeline",description:"Build native container image from a Containerfile or run local CI/CD workflow",aliases:["run-ci"],requiresAuth:!1,requiresRoom:!1,async execute(e,s){const t=e.includes("-t")||e.includes("--tag"),o=e[0]&&(e[0].endsWith(".yml")||e[0].endsWith(".yaml")),n=["Containerfile",BUILD_FILE].some(e=>fs.existsSync(path.join(process.cwd(),e)));if((t||n||e.includes("."))&&!o){let s="latest";const t=-1!==e.indexOf("-t")?e.indexOf("-t"):e.indexOf("--tag");if(-1!==t&&e[t+1])s=e[t+1];else{const t=e.filter(e=>!e.startsWith("-")&&"."!==e)[0];t&&(s=t)}console.log(chalk.cyan(`\nBuilding native container image with tag: ${chalk.bold(s)}...`));try{const e=new CapsuleBuilder(process.cwd()),t=await e.build(s);console.log(chalk.green(`\n[OK] Successfully built image "${s}" (ID: ${t.imageId})\n`))}catch(e){console.log(chalk.red(`\n[FAIL] Build error: ${e.message}\n`))}return}const r=e[0]||"tnc-ci.yml",l=process.cwd(),i=path.resolve(l,".tnc/workflows"),a=path.resolve(i,r);if(!a.startsWith(i+path.sep)&&a!==i)return void console.log(chalk.red("[FAIL] Security Error: Invalid workflow path outside .tnc/workflows"));if(!a.endsWith(".yml")&&!a.endsWith(".yaml"))return void console.log(chalk.red("[FAIL] Invalid workflow file format. Must be a .yml or .yaml file."));if(!fs.existsSync(a))return void console.log(chalk.red(`[FAIL] Workflow file not found: .tnc/workflows/${r}`));const c=path.basename(a);console.log(chalk.cyan(`\n Loading local CI/CD workflow: ${c}`));const u=fs.readFileSync(a,"utf8").split("\n");let h=c,d=[],f="";for(let e=0;e<u.length;e++){const s=u[e].trim();if(s.startsWith("name:"))h=s.substring(5).trim().replace(/['"]/g,"");else if(s.startsWith("-")){const t=s.substring(1).trim();if(t.startsWith("name:"))f=t.substring(5).trim().replace(/['"]/g,"");else if(t.startsWith("run:")){let s=t.substring(4).trim();if((s.startsWith('"')&&s.endsWith('"')||s.startsWith("'")&&s.endsWith("'"))&&(s=s.substring(1,s.length-1)),"|"===s){let t=[],o=e+1;for(;o<u.length;){const e=u[o],s=e.match(/^(\s+)(.*)/);if(!(s&&s[1].length>4))break;t.push(e.trim()),o++}s=t.join("\n"),e=o-1}d.push({name:f||`run: ${s.substring(0,30)}`,run:s}),f=""}}else if(s.startsWith("name:"))f=s.substring(5).trim().replace(/['"]/g,"");else if(s.startsWith("run:")){let t=s.substring(4).trim();if((t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.substring(1,t.length-1)),"|"===t){let s=[],o=e+1;for(;o<u.length;){const e=u[o],t=e.match(/^(\s+)(.*)/);if(!(t&&t[1].length>4))break;s.push(e.trim()),o++}t=s.join("\n"),e=o-1}d.push({name:f||`run: ${t.substring(0,30)}`,run:t}),f=""}}if(0===d.length)return void console.log(chalk.red("[FAIL] No build steps parsed from workflow"));const g=[];g.push(` [TNC Actions] Starting Local Workflow: "${h}" (${c})`),console.log(chalk.green(` [TNC Actions] Starting Local Workflow: "${h}" (${c})`));let p=!0;for(let e=0;e<d.length;e++){const s=d[e],t=`\n [Step ${e+1}/${d.length}] "${s.name}"\n$ ${s.run}`;g.push(t),console.log(chalk.yellow(t));const o=await new Promise(e=>{const t="win32"===process.platform?"cmd.exe":"/bin/sh",o="win32"===process.platform?["/s","/c",s.run]:["-c",s.run],n=spawn(t,o,{cwd:l,env:{...process.env,PATH:process.env.PATH,PUPPETEER_SKIP_DOWNLOAD:"true"}});n.stdout.on("data",e=>{const s=e.toString().trim();s&&s.split("\n").forEach(e=>{g.push(`[stdout] ${e}`),console.log(chalk.dim(` ${e}`))})}),n.stderr.on("data",e=>{const s=e.toString().trim();s&&s.split("\n").forEach(e=>{g.push(`[stderr] ${e}`),console.log(chalk.red(` ${e}`))})}),n.on("close",s=>{e(s)})});if(0!==o){p=!1;const e=`[FAIL] [Step Failed] Step "${s.name}" exited with status code: ${o}`;g.push(e),console.log(chalk.red(e));break}{const e=`[OK] [Step Passed] Step "${s.name}" completed successfully`;g.push(e),console.log(chalk.green(e))}}if(p){const e="\n Workflow ran successfully! status code: 0";g.push(e),console.log(chalk.green(e))}else{const e="\n[FAIL] Workflow failed!";g.push(e),console.log(chalk.red(e))}const m=s.ws.getCurrentRoom();if(m){process.stdout.write(chalk.dim(" Syncing stats to ThinkNCollab server...\r"));try{await s.api._request("POST",`/tnccicd/api/save-run/${m}`,{workflowFile:c,status:p?"passed":"failed",logs:g}),console.log(chalk.green(" [OK] Pipeline stats successfully synced to workspace!"))}catch(e){console.log(chalk.red(` [WARN] Failed to sync stats to server: ${e.message}`))}}}};
|
|
@@ -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 chalk=require("chalk"),crypto=require("crypto"),cp=require("child_process"),path=require("path");function getFetchFn(){return globalThis.fetch||require("node-fetch")}function parseCookieHeader(e,t){const o=new URL(t).hostname,s=[],n=e.split(/,(?=[^ ][^=]+=)/);for(const e of n){const t=e.split(";").map(e=>e.trim()),[n,...i]=t,a=n.indexOf("=");if(-1===a)continue;const r=n.slice(0,a).trim(),l=n.slice(a+1).trim(),c={};for(const e of i){const[t,o]=e.split("=").map(e=>e.trim());c[t.toLowerCase()]=o||!0}s.push({name:r,value:l,domain:c.domain||o,path:c.path||"/",httpOnly:!!c.httponly,secure:!!c.secure,sameSite:"None"===c.samesite?"None":"Strict"===c.samesite?"Strict":"Lax"})}return s}function extractCsrf(e,t){if(t){const o=t.match(/name=['"]([^'"]+)['"]/);if(o){const t=new RegExp(`name=["']${o[1]}["'][^>]*value=["']([^"']+)["']|value=["']([^"']+)["'][^>]*name=["']${o[1]}["']`),s=e.match(t);if(s)return{field:o[1],token:s[1]||s[2]}}}const o=[{field:"_csrf",re:/name=["']_csrf["'][^>]*value=["']([^"']+)["']/},{field:"_token",re:/name=["']_token["'][^>]*value=["']([^"']+)["']/},{field:"authenticity_token",re:/name=["']authenticity_token["'][^>]*value=["']([^"']+)["']/},{field:"__RequestVerificationToken",re:/name=["']__RequestVerificationToken["'][^>]*value=["']([^"']+)["']/}];for(const{field:t,re:s}of o){const o=e.match(s);if(o)return{field:t,token:o[1]}}return null}async function authViaRequest(e,t,o,s,n){const i=getFetchFn(),a=n.signinUrl||"/signin",r=t.replace(/\/$/,"")+a;console.log(chalk.dim(` → [request] GET ${r}`));const l=await i(r,{headers:{"User-Agent":"Mozilla/5.0 (compatible; TNCBot/1.0)"},redirect:"follow"}),c=await l.text(),d=l.headers.get("set-cookie")||"",u=extractCsrf(c,n.csrfSelector);u&&console.log(chalk.dim(` → CSRF detected: ${u.field}`));const h=new URLSearchParams({email:o,password:s});u&&h.append(u.field,u.token),console.log(chalk.dim(` → [request] POST ${r}`));const p=await i(r,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Cookie:d,"User-Agent":"Mozilla/5.0 (compatible; TNCBot/1.0)",Referer:r},body:h.toString(),redirect:"manual"});let m=p.headers.get("set-cookie");if(!m&&p.status>=300&&p.status<400){const e=p.headers.get("location");e&&(m=(await i(e.startsWith("http")?e:t+e,{headers:{Cookie:d},redirect:"manual"})).headers.get("set-cookie"))}if(!m)throw new Error('Auth failed — no session cookie returned. Check testUser credentials or switch to strategy: "form"');const g=parseCookieHeader(m,t);if(!g.length)throw new Error("Could not parse session cookies from response");await e.addCookies(g),console.log(chalk.dim(` → Cookies injected: ${g.map(e=>e.name).join(", ")}`))}async function authViaForm(e,t,o,s,n){const i=n.signinUrl||"/signin";if(await e.goto(t+i,{waitUntil:"networkidle",timeout:15e3}),console.log(chalk.dim(` → [form] ${e.url()}`)),!e.url().includes(i.replace(/^\//,"")))return;await e.evaluate(()=>{document.querySelectorAll("form input, form button, form > div, form > section").forEach(e=>{e.style.display="",e.style.visibility="visible",e.style.opacity="1"})});const a=n.fields||{},r=a.email||['input[type="email"]','input[name="email"]','input[id="email"]','input[autocomplete="email"]'].join(", "),l=a.password||['input[type="password"]','input[name="password"]','input[id="password"]','input[autocomplete="current-password"]'].join(", "),c=a.submit||['button[type="submit"]','input[type="submit"]','button:has-text("Sign in")','button:has-text("Login")','button:has-text("Log in")','button:has-text("Continue")',"form button"].join(", ");await e.locator(r).first().fill(o),console.log(chalk.dim(" → Email filled")),await e.locator(l).first().fill(s),console.log(chalk.dim(" → Password filled")),await e.locator(c).first().count().catch(()=>0)?(await e.locator(c).first().click(),console.log(chalk.dim(" → Submit clicked"))):(await e.locator(l).first().press("Enter"),console.log(chalk.dim(" → Submitted via Enter")));const d=n.successRedirect||null;if(await e.waitForURL(e=>d?e.includes(d):!e.includes("/signin")&&!e.includes("/login")&&!e.includes("/verify"),{timeout:15e3}).catch(()=>{}),e.url().includes("/signin")||e.url().includes("/login")){const t=await e.locator('.flash, .error, .alert, [class*="error"], [role="alert"]').first().textContent().catch(()=>null);throw new Error("Auth failed — still on signin."+(t?` Page: "${t.trim()}"`:" Check credentials."))}console.log(chalk.dim(` → Redirected: ${e.url()}`))}async function authViaCookie(e,t,o){const s=o.cookies;if(!Array.isArray(s)||!s.length)throw new Error('auth.strategy "cookie" requires auth.cookies array in testConfig');const n=new URL(t).hostname,i=s.map(e=>({name:e.name,value:e.value,domain:e.domain||n,path:e.path||"/",httpOnly:e.httpOnly||!1,secure:e.secure||!1,sameSite:e.sameSite||"Lax"}));await e.addCookies(i),console.log(chalk.dim(` → Cookies injected: ${i.map(e=>e.name).join(", ")}`))}async function authenticate(e,t,o,s,n={}){const i=n.strategy||"request";switch(console.log(chalk.dim(` → Auth strategy: ${i}`)),i){case"request":await authViaRequest(t,o,s.email,s.password,n);break;case"form":await authViaForm(e,o,s.email,s.password,n);break;case"cookie":await authViaCookie(t,o,n);break;case"skip":console.log(chalk.dim(" → Auth skipped"));break;default:throw new Error(`Unknown auth strategy: "${i}". Use: request | form | cookie | skip`)}}async function runHttpTest(e,t,o={}){const{baseUrl:s,method:n="GET",endpoint:i,headers:a={},body:r}=e;if(!i&&!s)throw new Error("testConfig missing baseUrl or endpoint");let l;if(i&&/^https?:\/\//i.test(i))l=i;else if(s){const e=(i||"").startsWith("/")?i:`/${i||""}`;l=s.replace(/\/+$/,"")+e}else l=i;const c=getFetchFn();let d,u,h="";if(t&&"skip"!==o.strategy){const e=s.replace(/\/$/,"")+(o.signinUrl||"/signin"),n=await c(e,{redirect:"follow"}),i=await n.text(),a=n.headers.get("set-cookie")||"",r=extractCsrf(i,o.csrfSelector),l=new URLSearchParams({email:t.email,password:t.password});r&&l.append(r.field,r.token);const d=(await c(e,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded",Cookie:a},body:l.toString(),redirect:"manual"})).headers.get("set-cookie");d?(h=d.split(",").map(e=>e.split(";")[0].trim()).join("; "),console.log(chalk.dim(" → HTTP auth: cookie obtained"))):console.log(chalk.yellow(" [WARN] HTTP auth: no cookie returned"))}try{d=await c(l,{method:n,headers:{"Content-Type":"application/json",...h?{Cookie:h}:{},...a},...r&&"GET"!==n?{body:JSON.stringify(r)}:{}}),u=await d.json().catch(()=>({}))}catch(e){throw new Error(`Could not reach ${l} — is your server running? (${e.message})`)}return{status:d.status,body:u}}async function runBrowserTest(e,t,o={}){let s;try{s=require("playwright")}catch{throw new Error("Playwright not installed.\n Run: npm install -g playwright && npx playwright install chromium")}const{url:n,flow:i=[],expect:a,auth:r={}}=e;if(!n)throw new Error("testConfig missing url");const l={...o,...r},c=new URL(n).origin,d=await s.chromium.launch({headless:!0}),u=await d.newContext(),h=await u.newPage();try{const e=l.strategy||(t?"request":"skip");if("cookie"===e||t&&"skip"!==e?await authenticate(h,u,c,t,l):t||"skip"===e||console.log(chalk.yellow(" [WARN] No testUser — auth skipped")),await h.goto(n,{waitUntil:"networkidle",timeout:15e3}),!(n.includes("/signin")||n.includes("/login")||n.includes("/signup"))&&(h.url().includes("/signin")||h.url().includes("/login")))throw new Error("Auth failed — redirected to signin on target page");console.log(chalk.dim(` → Target: ${h.url()}`));for(const e of i)switch(e.action){case"fill":await h.fill(e.selector,e.value||"");break;case"click":await h.click(e.selector);break;case"wait":await h.waitForTimeout(e.ms||1e3);break;case"waitForElement":await h.waitForSelector(e.selector,{timeout:8e3});break;case"select":await h.selectOption(e.selector,e.value);break;case"screenshot":await h.screenshot({path:e.path||"/tmp/tnc.png"})}const o=h.url(),s=await h.textContent("body").catch(()=>"");let r=!1,d="";if(a?.element)try{await h.waitForSelector(a.element,{timeout:5e3}),r=!0,d=await h.textContent(a.element).catch(()=>"")}catch{r=!1}return{url:o,elementExists:r,elementText:d,bodyText:s}}finally{await d.close()}}function runLocalCommand(e){return new Promise(t=>{cp.exec(e,(e,o,s)=>{const n=e?e.code||1:0;t({exitCode:n,stdout:o.toString(),stderr:s.toString()})})})}function judgeLocally(e,t){const{type:o,value:s,condition:n,expect:i}=e,a=[];try{switch(o){case"exitCode":{const e=t.exitCode===s;return{passed:e,reason:e?`Exit code matched: ${s}`:`Expected exit code ${s}, got ${t.exitCode}`}}case"stdout":{const e=(t.stdout||"").trim(),o=(s||"").trim(),n=e===o;return{passed:n,reason:n?"Output matched":"Output mismatch",diff:n?void 0:{expected:o,actual:e}}}case"regex":{const e=new RegExp(s),o=t.stdout||t.output||"",n=e.test(o);return{passed:n,reason:n?`Regex matched: ${s}`:`Regex did not match: ${s}`}}case"http":if(void 0!==i?.status&&t.status!==i.status&&a.push(`Status: expected ${i.status}, got ${t.status}`),i?.body)for(const[e,o]of Object.entries(i.body)){const s=getNestedValue(t.body||{},e);o?.exists?null==s&&a.push(`body.${e}: expected to exist`):void 0!==o?.eq?s!==o.eq&&a.push(`body.${e}: expected "${o.eq}", got "${s}"`):void 0!==o?.contains&&(String(s).includes(String(o.contains))||a.push(`body.${e}: expected to contain "${o.contains}"`))}break;case"json":{const e=t.output||t.json||{};if(n)return judgeWithConditions(n,e);{const t=JSON.stringify(s)===JSON.stringify(e);return{passed:t,reason:t?"JSON matched":"JSON mismatch",diff:t?void 0:{expected:s,actual:e}}}}case"loadtest":case"geekload":{const e=t.output||t.json||{};return judgeWithConditions(n||i?.condition||i||{},e)}case"browser":if(i?.element&&!t.elementExists&&a.push(`Element "${i.element}" not found on page`),i?.text&&t.elementText&&(t.elementText.includes(i.text)||a.push(`Element text: expected to contain "${i.text}", got "${t.elementText}"`)),i?.redirectUrl)try{const e=new URL(t.url).pathname;e.includes(i.redirectUrl)||a.push(`URL: expected "${i.redirectUrl}", got "${e}"`)}catch{a.push(`Invalid URL in result: "${t.url}"`)}break;case"tnc-build":{const e=0===t.exitCode;return{passed:e,reason:e?"TNC Local CI pipeline passed":"TNC Local CI pipeline failed"}}default:return{passed:!1,reason:`Unknown test type: ${o}`}}return{passed:0===a.length,reason:0===a.length?"All checks passed":a.join(" | "),failures:a}}catch(e){return{passed:!1,reason:`Judge error: ${e.message}`}}}function judgeWithConditions(e,t){const o=[];for(const[s,n]of Object.entries(e)){const e=t[s];if(void 0!==e)for(const[t,i]of Object.entries(n)){let n=!1;switch(t){case"gte":n=e>=i;break;case"lte":n=e<=i;break;case"gt":n=e>i;break;case"lt":n=e<i;break;case"eq":n=e===i;break;case"contains":n=String(e).includes(String(i));break;case"regex":n=new RegExp(i).test(String(e));break;default:n=!1}n||o.push(`"${s}" failed: ${e} ${t} ${i}`)}else o.push(`Missing key: "${s}"`)}return{passed:0===o.length,reason:0===o.length?"All conditions passed":`Failed conditions: ${o.join("; ")}`,diff:o.length>0?{conditions:e,actual:t}:void 0}}function getNestedValue(e,t){return t.split(".").reduce((e,t)=>e&&void 0!==e[t]?e[t]:void 0,e)}async function hitJudgeVerify(e,t,o,s){const n=getFetchFn(),i={"Content-Type":"application/json"};o&&(i.Authorization=`Bearer ${o}`,i["x-shell-token"]=o);const a=await n(`${e}/tasks/${t}/api/verify`,{method:"POST",headers:i,body:JSON.stringify(s)}),r=await a.json().catch(()=>({}));if(!a.ok)throw new Error(r.message||r.error||`Verification failed: ${a.status}`);return r}module.exports={name:"task-submit",description:"Auto-test and submit a task for verification",aliases:["submit"],requiresAuth:!0,async execute(e,t){if(!e[0])return console.log(chalk.red("Usage: task-submit <task-id>")),void console.log(chalk.dim(" Alias: submit <task-id>"));const o=e[0],s=t.api.apiUrl||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com";let n;try{n=await t.api._request("GET",`/thinknsh/${o}/cli`)}catch(e){return void(e.message?.includes("404")?console.log(chalk.red("[FAIL] Task not found")):e.message?.includes("403")?console.log(chalk.red("[FAIL] Access denied")):console.log(chalk.red(`[FAIL] ${e.message}`)))}if(!n.testConfig)return console.log(chalk.yellow("[WARN] This task has no test config defined.")),void console.log(chalk.dim(" complete <task-id> to mark manually\n"));const{title:i,testConfig:a,webhookSecret:r}=n,l=n.testuserEmail&&n.testuserPassword?{email:n.testuserEmail,password:n.testuserPassword}:null,c={strategy:n.testauthStrategy||"request",signinUrl:n.testauthSigninUrl||"/signin",csrfSelector:n.testauthCsrf||null};console.log(chalk.cyan(`\n ── Submitting: "${i}" ──────────────────────`)),console.log(chalk.dim(` Type : ${a.type}`));const d={...c,...a.auth||{}};if("http"===a.type){const e=a.endpoint&&/^https?:\/\//i.test(a.endpoint)?a.endpoint:`${(a.baseUrl||"").replace(/\/+$/,"")}${(a.endpoint||"").startsWith("/")?a.endpoint:`/${a.endpoint||""}`}`;console.log(chalk.dim(` Target : ${a.method||"GET"} ${e}`))}else if("browser"===a.type){const e=a.auth?.strategy||(l?"request":"skip");console.log(chalk.dim(` Target : ${a.url}`)),console.log(chalk.dim(` Auth : ${e}`)),l?console.log(chalk.dim(` TestUser : ${l.email}`)):console.log(chalk.yellow(" [WARN] No testUser — auth will be skipped"))}let u;console.log(chalk.dim(" Running virtual user...\n"));try{if("http"===a.type)u=await runHttpTest(a,l,d),console.log(chalk.dim(` → Status : ${u.status}`)),console.log(chalk.dim(` → Body : ${JSON.stringify(u.body).slice(0,80)}`));else if("browser"===a.type)console.log(chalk.dim(" → Launching headless browser...")),u=await runBrowserTest(a,l,d),console.log(chalk.dim(` → Page : ${u.url}`)),console.log(chalk.dim(" → Element: "+(u.elementExists?"found [OK]":"not found [FAIL]"))),u.elementText&&console.log(chalk.dim(` → Text : "${u.elementText.trim().slice(0,60)}"`));else if("exitCode"===a.type||"stdout"===a.type||"regex"===a.type){const e=a.command||a.script;if(!e)throw new Error(`testConfig type "${a.type}" requires a "command" or "script" to execute.`);console.log(chalk.dim(` → Executing local command: "${e}"`));const t=await runLocalCommand(e);u={exitCode:t.exitCode,stdout:t.stdout,stderr:t.stderr},console.log(chalk.dim(` → Exit Code: ${u.exitCode}`)),u.stdout&&console.log(chalk.dim(` → Stdout : ${u.stdout.trim().slice(0,80)}`))}else if("json"===a.type){const e=a.command||a.script;if(!e)throw new Error('testConfig type "json" requires a "command" or "script" to output JSON.');{console.log(chalk.dim(` → Executing local command: "${e}"`));const t=await runLocalCommand(e);let o={};try{o=JSON.parse(t.stdout.trim())}catch(e){throw new Error(`Command executed but stdout was not valid JSON: ${t.stdout}`)}u={exitCode:t.exitCode,stdout:t.stdout,stderr:t.stderr,json:o,output:o}}}else if("loadtest"===a.type||"geekload"===a.type){const e=a.script||a.command;if(!e)throw new Error(`testConfig type "${a.type}" requires a "script" or "command" specifying the load test script path.`);const t=path.resolve(process.cwd(),e);console.log(chalk.dim(` → Running GeekLoad test script: ${e}`));const{runLoadTest:o}=require("../../core/geekload"),s=await o(t),n=new(require("cli-table3"))({head:[chalk.cyan("Metric"),chalk.cyan("Value")],colWidths:[25,20]});n.push(["Virtual Users",s.virtual_users],["Duration (sec)",s.duration_sec],["Total Requests",s.total_requests],["Failed Requests",s.failed_requests],["Failure Rate",`${(100*s.failure_rate).toFixed(2)}%`],["p95 Latency (ms)",s.p95_latency_ms]),console.log("\n"+n.toString()+"\n"),u={exitCode:s.failed_requests>0?1:0,output:s,json:s}}else{if("tnc-build"!==a.type)return void console.log(chalk.red(`[FAIL] Unsupported test type: ${a.type}`));{const e=require("fs"),t=require("path"),{spawn:o}=require("child_process"),s=process.cwd(),n=t.join(s,".tnc/workflows");if(!e.existsSync(n))throw new Error("No .tnc/workflows directory found in project root.");const i=e.readdirSync(n).filter(e=>e.endsWith(".yml")||e.endsWith(".yaml"));if(0===i.length)throw new Error("No workflow files found in .tnc/workflows/");const a=i[0];console.log(chalk.dim(` → Found workflow file: ${a}`));const r=e.readFileSync(t.join(n,a),"utf8").split("\n");let l=a,c=[],d="";for(let e=0;e<r.length;e++){const t=r[e].trim();if(t.startsWith("name:"))l=t.substring(5).trim().replace(/['"]/g,"");else if(t.startsWith("-")){const o=t.substring(1).trim();if(o.startsWith("name:"))d=o.substring(5).trim().replace(/['"]/g,"");else if(o.startsWith("run:")){let t=o.substring(4).trim();if((t.startsWith('"')&&t.endsWith('"')||t.startsWith("'")&&t.endsWith("'"))&&(t=t.substring(1,t.length-1)),"|"===t){let o=[],s=e+1;for(;s<r.length;){const e=r[s],t=e.match(/^(\s+)(.*)/);if(!(t&&t[1].length>4))break;o.push(e.trim()),s++}t=o.join("\n"),e=s-1}c.push({name:d||`run: ${t.substring(0,30)}`,run:t}),d=""}}else if(t.startsWith("name:"))d=t.substring(5).trim().replace(/['"]/g,"");else if(t.startsWith("run:")){let o=t.substring(4).trim();if((o.startsWith('"')&&o.endsWith('"')||o.startsWith("'")&&o.endsWith("'"))&&(o=o.substring(1,o.length-1)),"|"===o){let t=[],s=e+1;for(;s<r.length;){const e=r[s],o=e.match(/^(\s+)(.*)/);if(!(o&&o[1].length>4))break;t.push(e.trim()),s++}o=t.join("\n"),e=s-1}c.push({name:d||`run: ${o.substring(0,30)}`,run:o}),d=""}}if(0===c.length)throw new Error("No steps found in the workflow file.");console.log(chalk.cyan(`\n [TNC Actions] Starting Local Workflow: "${l}"\n`));const h=[];let p=!0,m="";for(let e=0;e<c.length;e++){const t=c[e];console.log(chalk.cyan(` [Step ${e+1}/${c.length}] "${t.name}"`)),console.log(chalk.gray(`$ ${t.run}`));const n=await new Promise(e=>{const n="win32"===process.platform?"cmd.exe":"/bin/sh",i="win32"===process.platform?["/s","/c",t.run]:["-c",t.run],a=o(n,i,{cwd:s,env:{...process.env,PATH:process.env.PATH,PUPPETEER_SKIP_DOWNLOAD:"true"}});a.stdout.on("data",e=>{const t=e.toString();process.stdout.write(t),m+=t}),a.stderr.on("data",e=>{const t=e.toString();process.stderr.write(chalk.red(t)),m+=t}),a.on("close",t=>{e(t)})}),i=0===n;if(h.push({name:t.name,command:t.run,exitCode:n,passed:i}),!i){p=!1,console.log(chalk.red(`\n[FAIL] Step "${t.name}" failed with exit code: ${n}. Aborting.\n`));break}console.log(chalk.green(`\n[OK] Step "${t.name}" passed successfully.\n`))}u={exitCode:p?0:1,stdout:m,stderr:"",output:{workflow:l,status:p?"passed":"failed",stepsCount:c.length,stepsRun:h},json:{workflow:l,status:p?"passed":"failed",stepsCount:c.length,stepsRun:h}}}}}catch(e){return void console.log(chalk.red(`\n [FAIL] Virtual user failed: ${e.message}\n`))}const h=judgeLocally(a,u),p=h.passed?chalk.green("─".repeat(50)):chalk.red("─".repeat(50)),m=h.passed?chalk.bgGreen.black(" PASS "):chalk.bgRed.white(" FAIL ");console.log("\n"+p),console.log(` ${m} ${chalk.bold(i)}`),console.log(` ${chalk.dim("Result :")} ${h.passed?chalk.green(h.reason):chalk.red(h.reason)}`),console.log(p);try{console.log(chalk.dim("\n Syncing verdict with Server-Side Judge..."));const e=t.api?.getToken?.()||t.config?.shellToken||null;let n=null;if(t.sandboxEngine){const e=t.sandboxEngine.list(),o=e.find(e=>e.id===t.activeSandbox)||e[0];o&&(n=`${s}/thinknsh/proxy/${o.id}`)}const i=await hitJudgeVerify(s,o,e,{comment:`Verified via ThinkNSH CLI (${a.type}): ${h.reason}`,payload:u,tunnelUrl:n||void 0}),r=i?.runNumber?` (Run #${i.runNumber}${i.durationMs?", "+i.durationMs+"ms":""})`:"";console.log(chalk.green(` [OK] Verdict verified & saved on ThinkNCollab Server!${r}\n`))}catch(e){console.log(chalk.yellow(` [WARN] Server Judge Sync: ${e.message}`)),console.log(chalk.dim(" Local result shown above is accurate.\n"))}t.pushNotification({type:"verdict",passed:h.passed,title:i,reason:h.reason,taskId:o,at:(new Date).toISOString()})},runBrowserTest};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"create-task",description:"Create a new task in current room",aliases:["newtask","addtask"],requiresAuth:!0,requiresRoom:!0,async execute(e,a){const{input:t,select:s,confirm:i}=require("@inquirer/prompts"),o=a.ws.getCurrentRoom();try{const{columns:e}=await a.api._request("GET",`/thinknsh/${o}/cli/columns`);if(!e?.length)return void console.log(chalk.red("[FAIL] No columns found in this room's board"));console.log(chalk.cyan("\n // create-task\n"));const n=await t({message:"Task title:",validate:e=>e.trim().length>=3||"Min 3 characters"}),r=await t({message:"Description (optional):"}),c=await s({message:"Column:",choices:e.map(e=>({name:e.title,value:e._id}))}),l=await s({message:"Priority:",choices:[{name:"low",value:"low"},{name:"medium",value:"medium"},{name:"high",value:"high"}],default:"medium"}),m=["Bugs","Feature Requests","Improvements / Enhancements","Production Critical Issues","Security Issues","Performance Issues","UI/UX Issues","Testing & QA","DevOps & Infrastructure","Refactoring & Code Quality","Documentation","Other"],u=await s({message:"Category:",choices:m.map(e=>({name:e,value:e}))}),d=await t({message:"Due date (YYYY-MM-DD, optional):",validate:e=>!e||!isNaN(Date.parse(e))||"Invalid date format"});if(!await i({message:"Create task?",default:!0}))return void console.log(chalk.dim(" Cancelled."));const h={title:n.trim(),description:r.trim()||void 0,columnId:c,priority:l,category:"Other"===u?void 0:u,dueDate:d?new Date(d).toISOString():void 0,source:"cli"},g=await a.api._request("POST",`/thinknsh/${o}/cli/tasks`,h);console.log(chalk.green(`\n[OK] Task created: "${g.task.title}"`)),console.log(chalk.dim(` ID: ${g.task._id}`)),console.log(chalk.dim(` Column: ${g.task.columnId?.title||c}\n`)),a.pushNotification({type:"notification",level:"success",title:"Task Created",message:g.task.title})}catch(e){e.message?.includes("403")?console.log(chalk.red("[FAIL] You are not a member of this room")):console.log(chalk.red(`[FAIL] ${e.message}`))}}};
|
|
@@ -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 chalk=require("chalk"),path=require("path"),fs=require("fs-extra"),{runLoadTest}=require("../../core/geekload"),Table=require("cli-table3");module.exports={name:"loadtest",description:"Run a local load test script using GeekLoad and sync stats to TNC",aliases:["geekload"],requiresAuth:!0,requiresRoom:!0,async execute(e,s){if(!e[0])return void console.log(chalk.red("Usage: loadtest <script-path>"));const t=path.resolve(process.cwd(),e[0]);if(fs.existsSync(t)){console.log(chalk.cyan(`\n Loading test script: ${e[0]}`));try{const a=await runLoadTest(t),l=new Table({head:[chalk.green("Metric"),chalk.green("Value")],colWidths:[25,20]});l.push(["Virtual Users",a.virtual_users],["Duration (sec)",a.duration_sec],["Total Requests",a.total_requests],["Failed Requests",a.failed_requests],["Requests/sec (RPS)",a.requests_per_sec],["Failure Rate",`${(100*a.failure_rate).toFixed(2)}%`],["Average Latency (ms)",a.avg_latency_ms],["p95 Latency (ms)",a.p95_latency_ms],["p99 Latency (ms)",a.p99_latency_ms]),console.log(l.toString());const o=s.ws.getCurrentRoom();if(o){process.stdout.write(chalk.dim(" Syncing stats to ThinkNCollab server...\r"));try{const t={targetUrl:e[0],duration:a.duration_sec,connections:a.virtual_users,requestsTotal:a.total_requests,throughputTotalBytes:512*a.total_requests,latency:{p50:a.avg_latency_ms,p90:a.p95_latency_ms,p99:a.p99_latency_ms,average:a.avg_latency_ms,min:.5*a.avg_latency_ms,max:1.5*a.avg_latency_ms},errors:a.failed_requests};await s.api._request("POST",`/tncloadtest/api/save-run/${o}`,{targetUrl:e[0],duration:a.duration_sec,connections:a.virtual_users,stats:t}),console.log(chalk.green(" [OK] Load test stats successfully synced to workspace!"))}catch(e){console.log(chalk.red(` [WARN] Failed to sync stats to server: ${e.message}`))}}a.failures.length>0&&(console.log(chalk.yellow(`\n[WARN] Recorded Failures (${a.failures.length}):`)),a.failures.slice(0,5).forEach((e,s)=>{console.log(chalk.dim(` ${s+1}. [${e.method}] ${e.url}`)),e.failures.forEach(e=>{console.log(chalk.red(` - ${e}`))})}),a.failures.length>5&&console.log(chalk.dim(` ... and ${a.failures.length-5} more failures`)))}catch(e){console.log(chalk.red(`[FAIL] Load test execution failed: ${e.message}`))}}else console.log(chalk.red(`[FAIL] Script not found: ${e[0]}`))}};
|
|
@@ -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 chalk=require("chalk"),STATUS_MAP={accept:"accepted",start:"inprogress",complete:"completed",reject:"rejected",done:"completed","task-status":null};module.exports={name:"task-status",description:"Update your task status",aliases:["accept","start","complete","reject","done"],requiresAuth:!0,requiresRoom:!0,async execute(e,t,s){const a=t.ws.getCurrentRoom(),c=e[0],l=s||t._lastCommand||"task-status",o=STATUS_MAP[l]??e[1];if(c)if(o)try{const e=await t.api._request("POST",`/thinknsh/${a}/tasks/${c}/status`,{status:o}),s={accepted:chalk.green,inprogress:chalk.blue,completed:chalk.green,rejected:chalk.red}[o]||chalk.white;console.log(s(`\n [OK] Task "${e.taskTitle}" → ${o}`)),e.allCompleted?console.log(chalk.green(" All assignees done — task fully completed!\n")):console.log(""),t.ws.isConnected?.()&&t.ws.socket&&t.ws.socket.emit("task:"+("inprogress"===o?"started":o),{roomId:a,taskId:c,taskTitle:e.taskTitle,userId:t.api.getUser()?._id})}catch(e){console.log(chalk.red(`\n [FAIL] ${e.message}\n`))}else console.log(chalk.yellow(" Usage: task-status <taskId> <accepted|inprogress|completed|rejected>"));else console.log(chalk.yellow(" Usage: accept <taskId> | start <taskId> | complete <taskId>"))}};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"task-info",description:"View full task details including test config and webhook URL",aliases:["task","info","taskInfo","--t"],requiresAuth:!0,async execute(e,o){if(!e[0])return console.log(chalk.red("Usage: task-info <task-id>")),void console.log(chalk.dim(" Alias: info <task-id>"));const l=e[0];let c;try{c=await o.api._request("GET",`/thinknsh/${l}/cli`)}catch(e){return void(e.message?.includes("404")?console.log(chalk.red("[FAIL] Task not found")):e.message?.includes("403")?console.log(chalk.red("[FAIL] Access denied")):console.log(chalk.red(`[FAIL] ${e.message}`)))}const t={pending:chalk.yellow,inprogress:chalk.blue,review:chalk.magenta,completed:chalk.green,reopened:chalk.cyan},a={high:chalk.red,medium:chalk.yellow,low:chalk.green},n=t[c.status]||chalk.white,s=a[c.priority]||chalk.white;if(console.log(chalk.cyan("\n ─── Task Info ───────────────────────────────────")),console.log(` ${chalk.dim("id ")} ${chalk.dim(c._id)}${c.taskID?chalk.cyan(" #"+c.taskID):""}`),console.log(` ${chalk.dim("title ")} ${chalk.bold(c.title)}`),console.log(` ${chalk.dim("status ")} ${n(c.status)}`),console.log(` ${chalk.dim("priority")} ${s(c.priority)}`),c.category&&console.log(` ${chalk.dim("category")} ${chalk.dim(c.category)}`),c.dueDate&&console.log(` ${chalk.dim("due ")} ${chalk.yellow(new Date(c.dueDate).toLocaleDateString())}`),c.description&&(console.log(chalk.dim("\n description:")),c.description.split("\n").forEach(e=>console.log(` ${chalk.white(e)}`))),c.referenceLinks&&c.referenceLinks.length>0&&(console.log(chalk.cyan("\n ─── Supported Links ─────────────────────────────")),c.referenceLinks.forEach(e=>{console.log(` ${chalk.dim(e.title.padEnd(12))} ${chalk.blue(e.url)}`)})),c.assignedTo?.length&&(console.log(chalk.dim("\n assignees:")),c.assignedTo.forEach(e=>{const o=e.userId?.name||"Unknown",l="completed"===e.status?chalk.green("[OK]"):"accepted"===e.status?chalk.blue("●"):"rejected"===e.status?chalk.red("[FAIL]"):chalk.dim("○");console.log(` ${l} ${o} ${chalk.dim(`[${e.status}]`)}`)})),c.testConfig?.type){const e=c.testConfig;console.log(chalk.cyan("\n ─── Test Config ─────────────────────────────────")),console.log(` ${chalk.dim("type ")} ${chalk.bold(e.type)}`),"http"===e.type&&(console.log(` ${chalk.dim("method ")} ${chalk.green(e.method||"GET")}`),console.log(` ${chalk.dim("target ")} ${chalk.white((e.baseUrl||"")+(e.endpoint||""))}`),e.body&&Object.keys(e.body).length&&console.log(` ${chalk.dim("body ")} ${chalk.dim(JSON.stringify(e.body))}`),e.expect&&(console.log(chalk.dim("\n expect:")),void 0!==e.expect.status&&console.log(` ${chalk.dim("status")} ${chalk.green(e.expect.status)}`),e.expect.body&&Object.entries(e.expect.body).forEach(([e,o])=>{const l="object"==typeof o?Object.entries(o).map(([e,o])=>`${e}: ${o}`).join(", "):String(o);console.log(` ${chalk.dim("body."+e)} ${chalk.cyan(l)}`)}))),"browser"===e.type&&(console.log(` ${chalk.dim("url ")} ${chalk.white(e.url||"")}`),e.flow?.length&&(console.log(chalk.dim("\n flow:")),e.flow.forEach((e,o)=>{console.log(` ${chalk.dim(o+1+".")} ${chalk.green(e.action)} ${chalk.dim(e.selector||"")} ${e.value?chalk.white(`"${e.value}"`):""}`)})),e.expect&&(console.log(chalk.dim("\n expect:")),e.expect.element&&console.log(` ${chalk.dim("element ")} ${chalk.cyan(e.expect.element)}`),e.expect.text&&console.log(` ${chalk.dim("text ")} ${chalk.cyan(`contains "${e.expect.text}"`)}`),e.expect.redirectUrl&&console.log(` ${chalk.dim("redirectUrl")} ${chalk.cyan(e.expect.redirectUrl)}`))),"json"===e.type&&e.expect?.condition&&(console.log(chalk.dim("\n conditions:")),Object.entries(e.expect.condition).forEach(([e,o])=>{const l=Object.entries(o).map(([e,o])=>`${e} ${o}`).join(", ");console.log(` ${chalk.dim(e)} ${chalk.cyan(l)}`)})),"exitCode"===e.type&&console.log(` ${chalk.dim("expect ")} exitCode ${chalk.green(e.expect?.exitCode??0)}`),"stdout"===e.type&&console.log(` ${chalk.dim("expect ")} stdout "${chalk.green(e.expect?.stdout||"")}"`),"regex"===e.type&&console.log(` ${chalk.dim("expect ")} regex ${chalk.green(e.expect?.regex||"")}`),"tnc-build"===e.type&&console.log(` ${chalk.dim("runner ")} ${chalk.green("Local tnc-ci.yml workflow execution")}`)}else console.log(chalk.yellow("\n [WARN] No test config — task will need manual completion")),console.log(chalk.dim(" Use: complete <task-id>"));if(c.webhookSecret){const e=`${o.api.apiUrl||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com"}/webhooks/tasks/${l}/judge`;console.log(chalk.cyan("\n ─── Webhook ─────────────────────────────────────")),console.log(` ${chalk.dim("url ")} ${chalk.white(e)}`),console.log(` ${chalk.dim("secret ")} ${chalk.dim(c.webhookSecret)}`),console.log(` ${chalk.dim("header ")} ${chalk.dim("x-tnc-signature: sha256=<HMAC of body>")}`),console.log(chalk.dim("\n To submit from your project:")),console.log(chalk.cyan(" submit "+l))}if(c.lastVerdict?.at){const e=c.lastVerdict,o=e.passed,l=o?chalk.bgGreen.black(" PASS "):chalk.bgRed.white(" FAIL "),t=new Date(e.at).toLocaleString();console.log(chalk.cyan("\n ─── Last Verdict ────────────────────────────────")),console.log(` ${l} ${o?chalk.green(e.reason):chalk.red(e.reason)}`),console.log(` ${chalk.dim("at")} ${chalk.dim(t)}`),e.diff&&(console.log(` ${chalk.dim("expected")} ${chalk.cyan(JSON.stringify(e.diff.expected??e.diff.conditions))}`),console.log(` ${chalk.dim("actual ")} ${chalk.yellow(JSON.stringify(e.diff.actual))}`))}console.log(chalk.cyan(" ─────────────────────────────────────────────────\n"))}};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"tasks",description:"List your tasks + suggested tasks in the current room",aliases:["mytasks","my-tasks"],requiresAuth:!0,requiresRoom:!0,async execute(e,o){const l=o.api.getUser(),t=o.ws.getCurrentRoom();if(!l)return void console.log(chalk.red("[FAIL] Please login first"));if(!t)return void console.log(chalk.yellow("[WARN] Join a room first: join <room-id>"));const a=e.includes("--all")||e.includes("-a"),s=e.includes("--no-suggest")||e.includes("-ns"),c=e.find(e=>["pending","inprogress","review","completed","reopened"].includes(e)),i=e.find(e=>/^\d+$/.test(e))||5,n={pending:chalk.yellow,inprogress:chalk.blue,review:chalk.magenta,completed:chalk.green,reopened:chalk.cyan},r={critical:chalk.red,high:chalk.hex("#FF6B35"),medium:chalk.yellow,low:chalk.green},h=e=>{if(!e)return"";const o=new Date(e),l=Math.ceil((o-(new Date).setHours(0,0,0,0))/864e5),t=o.toLocaleDateString("en-IN",{day:"2-digit",month:"short"});return l<0?chalk.red(` due:${t}(!)`):0===l?chalk.yellow(" due:TODAY"):1===l?chalk.yellow(` due:${t}`):chalk.dim(` due:${t}`)},d=(e=chalk.cyan)=>e(" "+"─".repeat(58)),k=(e,o)=>` ${chalk.dim(e+":")} ${chalk.dim(o)}`;let g;process.stdout.write(chalk.dim(" Fetching tasks...\r"));try{g=await o.api._request("GET",`/thinknsh/${t}/tasks?n=${i}`)}catch(e){return void console.log(chalk.red(`\n [FAIL] ${e.message}\n`))}process.stdout.write(" ".repeat(30)+"\r");let m=g.tasks||[];if(c&&(m=m.filter(e=>e.status===c)),a||(m=m.filter(e=>!["completed","done"].includes(e.status))),console.log(d()),console.log(chalk.cyan(" MY TASKS")+chalk.dim(` (${m.length} active${g.total>m.length?` of ${g.total}`:""})`)),console.log(d()),m.length?m.forEach((e,o)=>{const l=n[e.status]||chalk.white,t=r[e.priority]||chalk.white,a=chalk.dim(` ${String(o+1).padStart(2)}.`),s=l(`[${(e.status||"unknown").padEnd(10)}]`),c="completed"===e.status?chalk.dim(e.title):chalk.white(e.title),i=h(e.dueDate),d=e.myStatus&&e.myStatus!==e.status?chalk.dim(` (${e.myStatus})`):"",g=e.column?chalk.dim(` [${e.column}]`):"";if(console.log(`\n${a} ${s} ${c}`),console.log(k("id",`${e.taskID?"#"+e.taskID:e._id}`)+` ${chalk.dim("priority:")} ${t(e.priority)}`+i+d+g),e.description){const o=e.description.length>90?e.description.slice(0,87)+"...":e.description;console.log(` ${chalk.dim(o)}`)}}):console.log(chalk.dim("\n No assigned tasks.\n")),!s){const e=g.suggestions?.tasks||[],o=g.suggestions?.insights||{},l=g.suggestions?.metadata||{};console.log("\n"+d(chalk.magenta)),console.log(chalk.magenta(" SUGGESTED FOR YOU")+chalk.dim(` (top ${e.length}${l.totalScored?` of ${l.totalScored} available`:""})`)),console.log(d(chalk.magenta)),e.length?(e.forEach((e,o)=>{const l=r[e.priority]||chalk.white,t=(a=e.priorityLevel,{CRITICAL:chalk.red,HIGH:chalk.hex("#FF6B35"),MEDIUM:chalk.yellow,LOW:chalk.green,"VERY LOW":chalk.dim}[a]||chalk.white);var a;const s=h(e.dueDate),c=e.column?chalk.dim(` [${e.column}]`):"",i=e.assigneeCount?chalk.dim(` · ${e.assigneeCount} assigned`):chalk.dim(" · unassigned"),n=e.isOverdue?chalk.red(" [WARN] OVERDUE"):e.isUrgent?chalk.yellow(" ⏰ URGENT"):"",d=chalk.dim(` ${String(o+1).padStart(2)}.`),g=`[score:${m=e.score,m>=80?chalk.red(m):m>=60?chalk.yellow(m):chalk.green(m)}]`;var m;const u=t(`[${e.priorityLevel}]`);console.log(`\n${d} ${g} ${u} ${chalk.white(e.title)}${n}`),console.log(k("id",e._id)+` ${chalk.dim("priority:")} ${l(e.priority)}`+s+c+i),e.priorityReason&&console.log(` ${chalk.dim("↳ "+e.priorityReason)}`)}),console.log("\n"+d(chalk.dim)),o.focusRecommendation&&console.log(` ${chalk.magenta("")} ${chalk.white(o.focusRecommendation)}`),o.estimatedTime&&console.log(` ${chalk.dim("⏱ Est. time:")} ${chalk.white(o.estimatedTime)}`),l.scoreRange&&console.log(` ${chalk.dim(" Score range:")} ${chalk.white(`${l.scoreRange.min}–${l.scoreRange.max}`)} ${chalk.dim("avg:")} ${chalk.white(l.scoreRange.avg)}`)):console.log(chalk.dim("\n No suggestions — all tasks are assigned.\n"))}console.log("\n"+d()),console.log(chalk.dim(" task <id> · complete <id> · tasks --all · tasks 10 · tasks --no-suggest")),console.log(d()+"\n")}};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"invite",description:"Invite someone to your team",aliases:["inv"],requiresAuth:!0,async execute(e,i){if(0===e.length)return console.log(chalk.red("[FAIL] Usage: invite <email> [--role <role>]")),void console.log(chalk.dim(" Roles: admin, member, viewer"));const o=e[0],n=e.indexOf("--role"),l=-1!==n&&e[n+1]?e[n+1]:"member";console.log(chalk.cyan(` Inviting ${o} as ${l}...`));try{const e=await i.api.inviteTeammate(o,l);console.log(chalk.green(`[OK] Invitation sent to ${o}`)),e.inviteLink&&console.log(chalk.dim(` Invite link: ${e.inviteLink}`))}catch(e){e.message.includes("already")?console.log(chalk.yellow(`[WARN] ${o} is already a team member`)):console.log(chalk.red(`[FAIL] Invitation failed: ${e.message}`))}}};
|
|
@@ -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 chalk=require("chalk"),Table=require("cli-table3");module.exports={name:"myteam",description:"Show team information and members",aliases:["team","org"],requiresAuth:!0,async execute(e,o){const l=e[0]||o.api.getUser()?.team;if(!l)return console.log(chalk.yellow("[WARN] You are not part of any team")),void console.log(chalk.dim(" Ask your team admin for an invite"));console.log(chalk.cyan(`\n Team Information: ${l}`)),console.log(chalk.dim("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));try{const e=await o.api.getTeamInfo(l);console.log(` ${chalk.yellow("Name:")} ${e.name}`),console.log(` ${chalk.yellow("Members:")} ${e.memberCount}`),console.log(` ${chalk.yellow("Rooms:")} ${e.roomCount}`),console.log(` ${chalk.yellow("Created:")} ${new Date(e.createdAt).toLocaleDateString()}`),e.description&&console.log(` ${chalk.yellow("Description:")} ${e.description}`),console.log();const a=new Table({head:["Status","Name","Email","Role","Current Room"],colWidths:[8,20,25,12,20]});e.members.forEach(e=>{const o=e.online?chalk.green("●"):chalk.gray("○"),l=e.currentRoom||"-";a.push([o,e.name||e.email.split("@")[0],e.email,e.role||"member",l])}),console.log(a.toString()),console.log(),e.recentActivity&&e.recentActivity.length>0&&(console.log(chalk.cyan(" Recent Activity:")),e.recentActivity.slice(0,5).forEach(e=>{const o=new Date(e.timestamp).toLocaleTimeString();console.log(chalk.dim(` [${o}] ${e.user}: ${e.action}`))}))}catch(e){console.log(chalk.red(`[FAIL] Failed to fetch team info: ${e.message}`))}}};
|
|
@@ -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 chalk=require("chalk");module.exports={name:"teammates",description:"List online teammates",aliases:["members","online"],requiresAuth:!0,async execute(e,l){const o=e[0]?e[0].toLowerCase():null;console.log(chalk.cyan("\n Online Teammates:")),console.log(chalk.dim("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"));try{const e=await l.api.getTeammates(),n=e.filter(e=>e.online),a=e.filter(e=>!e.online);0===n.length?console.log(chalk.yellow(" No teammates online")):n.filter(e=>!o||e.name.toLowerCase().includes(o)||e.email.toLowerCase().includes(o)).forEach(e=>{const l=e.typing?chalk.green(" (typing...)"):"",o=e.currentRoom?chalk.dim(` in ${e.currentRoom}`):"";console.log(` ${chalk.green("●")} ${chalk.white(e.name||e.email)}${l}${o}`),e.status&&"online"!==e.status&&console.log(chalk.dim(` ${e.status}`))}),a.length>0&&!o&&console.log(chalk.dim(`\n ... and ${a.length} offline`))}catch(e){console.log(chalk.red(`[FAIL] Failed to fetch teammates: ${e.message}`))}}};
|
|
@@ -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 chalk=require("chalk");async function cmdStart(e){if(e._shareActive)return void console.log(chalk.yellow('[Notice] Already sharing. Run "share stop" first.'));const s=e.ws.getCurrentRoom();if(s)return new Promise(r=>{const o=setTimeout(()=>{e.ws.socket.off("terminal:share:started",t),console.log(chalk.red("[Error] Share start timeout")),r()},6e3);function t(s){clearTimeout(o),e._shareActive=!0,e._shareSessionId=s.sessionId,console.log(chalk.green("\n[OK] Terminal sharing started")),console.log(chalk.dim(` Session : ${s.sessionId}`));const t=e.api&&e.api.apiUrl||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com";console.log(chalk.dim(` URL : ${t}/terminal/${s.sessionId}`)),console.log(chalk.dim("\n Room members can now view your terminal.")),console.log(chalk.dim(' Use "share grant <userId>" to give write access.\n')),_hookOutput(e,s.sessionId),r()}e.ws.socket.once("terminal:share:started",t),e.ws.socket.emit("terminal:share:start",{roomId:s})});console.log(chalk.red("[Error] Join a room first: join <room-id>"))}async function cmdStop(e){e._shareActive?(e.ws.socket.emit("terminal:share:stop"),_unhookOutput(e),e._shareActive=!1,e._shareSessionId=null,console.log(chalk.green("[OK] Terminal sharing stopped."))):console.log(chalk.yellow("[Notice] No active share session."))}async function cmdStatus(e){if(!e._shareActive||!e._shareSessionId)return void console.log(chalk.yellow("// No active share session"));const s=e._shareViewers||{},r=Object.values(s);console.log(chalk.cyan(`\n Terminal Share — ${e._shareSessionId}`)),console.log(chalk.dim(" "+"─".repeat(44))),r.length?r.forEach(e=>{const s=e.canWrite?chalk.blue("[write]"):chalk.dim("[read]"),r=e.requested?chalk.yellow(" ← requesting write"):"";console.log(` ${chalk.green("●")} ${chalk.white(e.name)} ${s}${r}`),console.log(chalk.dim(` userId: ${e.userId}`))}):console.log(chalk.dim(" No viewers yet.")),console.log()}async function cmdGrant(e,s){e?s._shareActive?(s.ws.socket.emit("terminal:grant",{sessionId:s._shareSessionId,userId:e}),s._shareViewers?.[e]&&(s._shareViewers[e].canWrite=!0,s._shareViewers[e].requested=!1),console.log(chalk.green(`[OK] Write access granted to ${e}`))):console.log(chalk.red("[Error] No active share session")):console.log(chalk.red("Usage: share grant <userId>"))}async function cmdRevoke(e,s){e?s._shareActive?(s.ws.socket.emit("terminal:revoke",{sessionId:s._shareSessionId,userId:e}),s._shareViewers?.[e]&&(s._shareViewers[e].canWrite=!1),console.log(chalk.green(`[OK] Write access revoked from ${e}`))):console.log(chalk.red("[Error] No active share session")):console.log(chalk.red("Usage: share revoke <userId>"))}async function cmdKick(e,s){e?s._shareActive?(s.ws.socket.emit("terminal:kick",{sessionId:s._shareSessionId,userId:e}),s._shareViewers&&delete s._shareViewers[e],console.log(chalk.green(`[OK] ${e} kicked from session`))):console.log(chalk.red("[Error] No active share session")):console.log(chalk.red("Usage: share kick <userId>"))}async function cmdLock(e){e._shareActive?(e._shareLocked=!0,e._shareViewers&&Object.values(e._shareViewers).forEach(e=>{e.canWrite=!1}),e.ws.socket.emit("terminal:lock",{sessionId:e._shareSessionId}),console.log(chalk.red.bold("\n[KILL-SWITCH ACTIVATED] All write access revoked and session locked immediately!")),console.log(chalk.dim(' No remote viewer can send input to your shell until you run "share unlock".\n'))):console.log(chalk.red("[Error] No active share session"))}async function cmdUnlock(e){e._shareActive?(e._shareLocked=!1,e.ws.socket.emit("terminal:unlock",{sessionId:e._shareSessionId}),console.log(chalk.green('\n[OK] Terminal share unlocked. Use "share grant <userId>" to grant write access.\n'))):console.log(chalk.red("[Error] No active share session"))}module.exports={name:"share",description:"Share your terminal with room members",aliases:["terminal-share","ts"],requiresAuth:!0,requiresRoom:!0,async execute(e,s){switch((e[0]||"").toLowerCase()){case"start":return cmdStart(s);case"stop":return cmdStop(s);case"status":return cmdStatus(s);case"grant":return cmdGrant(e[1],s);case"revoke":return cmdRevoke(e[1],s);case"kick":return cmdKick(e[1],s);case"lock":case"kill-switch":return cmdLock(s);case"unlock":return cmdUnlock(s);default:printHelp()}}};let _originalWrite=null;function _hookOutput(e,s){e._shareViewers={},e._shareWriterSeqs=new Map,e._shareLocked=!1,_originalWrite=process.stdout.write.bind(process.stdout),e.ws.socket.off("terminal:viewerJoined"),e.ws.socket.off("terminal:viewerLeft"),e.ws.socket.off("terminal:writeRequest"),e.ws.socket.off("terminal:input"),e.ws.socket.off("terminal:input:encrypted"),process.stdout.write=function(r,o,t){const n=r.toString();if(e.ws.isConnected()&&e._shareActive)if(e._e2eeEnabled&&e._aesKey){const r=require("../../core/crypto/e2ee-vault")._generateStreamNonce(s,e.ws.socket?.id||"host"),o=require("../../utils/terminalCrypto").encryptPayload(n,e._aesKey,{nonce:r.nonce,seq:r.seq});e.ws.socket.emit("terminal:output:encrypted",{sessionId:s,...o})}else e.ws.socket.emit("terminal:output",{sessionId:s,data:n});return _originalWrite(r,o,t)},e.ws.socket.on("terminal:viewerJoined",s=>{e._shareViewers||(e._shareViewers={}),e._shareViewers[s.userId]={userId:s.userId,name:s.name,canWrite:!1,requested:!1},console.log(chalk.dim(`\n [Viewer] ${s.name} is now viewing your terminal`))}),e.ws.socket.on("terminal:viewerLeft",s=>{e._shareViewers&&delete e._shareViewers[s.userId],console.log(chalk.dim(`\n [Viewer] ${s.name} stopped viewing`))}),e.ws.socket.on("terminal:writeRequest",s=>{e._shareViewers?.[s.userId]&&(e._shareViewers[s.userId].requested=!0),console.log(chalk.yellow(`\n [Request] ${s.name} is requesting write access`)),console.log(chalk.dim(` share grant ${s.userId} or share kick ${s.userId}\n`))}),e.ws.socket.on("terminal:input",s=>{if(e._shareLocked)return void console.log(chalk.red("\n [Warning] Dropped input: Session is locked (Kill-Switch Active)"));const r=s.userId||s.fromUserId||s.from,o=e._shareViewers?.[r];o&&o.canWrite?(console.log(chalk.blue(`\n [${s.fromName||"Writer"}] → ${s.input}`)),e.execute(s.input.trim())):console.log(chalk.red(`\n [Warning] Rejected unauthorized input from ${s.fromName||r} (No write permission)`))}),e.ws.socket.on("terminal:input:encrypted",s=>{if(e._shareLocked)return void console.log(chalk.red("\n [Security] Dropped encrypted input: Session is locked (Kill-Switch Active)"));const r=s.from||s.userId,o=e._shareViewers?.[r];if(!o||!o.canWrite)return void console.log(chalk.red(`\n [Security] Rejected unauthorized encrypted input from ${s.fromName||r}`));const t="number"==typeof s.seq?s.seq:0,n=e._shareWriterSeqs.get(r)||0;if(t<=n)return void console.log(chalk.red(`\n [Security Alert] Replay or reorder packet rejected! (seq ${t} <= lastSeq ${n}) from ${s.fromName||r}`));const i=s.roomId||e.ws.getCurrentRoom(),a=`term-input:${s.sessionId}:${r}:${i}:${t}`;try{const o=require("crypto"),n=require("../../core/crypto/e2ee-vault"),c=e._aesKey||n.getRoomKey(i),l=Buffer.from(s.iv,"hex"),h=Buffer.from(s.authTag,"hex"),d=Buffer.from(s.ciphertext,"hex"),u=o.createDecipheriv("aes-256-gcm",c,l);u.setAuthTag(h),u.setAAD(Buffer.from(a,"utf8"));const m=Buffer.concat([u.update(d),u.final()]).toString("utf8");e._shareWriterSeqs.set(r,t),console.log(chalk.blue(`\n [${s.fromName||"Writer"}] (E2EE seq:${t}) → ${m}`)),e.execute(m.trim())}catch(e){console.log(chalk.red(`\n [Security Alert] Tampered or forged terminal input rejected: ${e.message}`))}})}function _unhookOutput(e){_originalWrite&&(process.stdout.write=_originalWrite,_originalWrite=null),e.ws.socket.off("terminal:viewerJoined"),e.ws.socket.off("terminal:viewerLeft"),e.ws.socket.off("terminal:writeRequest"),e.ws.socket.off("terminal:input"),e.ws.socket.off("terminal:input:encrypted")}function printHelp(){console.log(chalk.cyan("\n Terminal Share Commands")),console.log(chalk.dim(" "+"─".repeat(40))),[["share start","Start sharing your terminal"],["share stop","Stop sharing"],["share status","Show viewers and write requests"],["share grant <userId>","Give write access to a viewer"],["share revoke <userId>","Revoke write access"],["share kick <userId>","Remove a viewer"],["share lock","Emergency kill-switch: lock session and drop all inputs"],["share unlock","Unlock terminal share"]].forEach(([e,s])=>{console.log(` ${chalk.green(e.padEnd(26))} ${chalk.dim(s)}`)}),console.log()}
|
|
@@ -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"),path=require("path"),fs=require("fs-extra"),os=require("os");class ThinkNCollabAPI{constructor(s={}){this.apiUrl=(s.apiUrl||process.env.THINKNCOLLAB_API_URL||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com").replace(/\/$/,""),this.wsUrl=(s.wsUrl||process.env.THINKNCOLLAB_WS_URL||process.env.THINKNCOLLAB_SERVER||"https://thinkncollab.com").replace(/\/$/,""),this.session=null,this.sessionPath=path.join(os.homedir(),".thinkncollab","session.json"),this._loadSession()}_loadSession(){try{fs.existsSync(this.sessionPath)&&(this.session=fs.readJsonSync(this.sessionPath))}catch{}}_saveSession(s){try{const e=path.dirname(this.sessionPath);fs.ensureDirSync(e,{mode:448}),fs.writeJsonSync(this.sessionPath,s,{spaces:2,mode:384});try{fs.chmodSync(this.sessionPath,384)}catch(s){}this.session=s}catch{}}_clearSession(){try{fs.removeSync(this.sessionPath)}catch{}this.session=null}isAuthenticated(){return!!this.session?.token}getToken(){return this.session?.token||null}getUser(){const s=this.session?.user;if(!s)return null;const e=(s._id||s.userId||"").toString();return{_id:e,userId:e,email:s.email||"",name:s.name||"",userType:s.userType||s.model||"User"}}_request(s,e,t=null,n={}){return new Promise((r,o)=>{const i=new URL(this.apiUrl+e),a="https:"===i.protocol,h=a?https:http,c=t?JSON.stringify(t):null,l={hostname:i.hostname,port:i.port||(a?443:80),path:i.pathname+i.search,method:s,headers:{"Content-Type":"application/json",Accept:"application/json",...this.session?.token?{"x-shell-token":this.session.token}:{},...n,...c?{"Content-Length":Buffer.byteLength(c)}:{}}},u=h.request(l,s=>{let e="";s.on("data",s=>e+=s),s.on("end",()=>{try{const t=JSON.parse(e);s.statusCode>=400?o(new Error(t.error||t.message||`HTTP ${s.statusCode}`)):r(t)}catch{o(new Error(`Invalid response: ${e.slice(0,100)}`))}})});u.on("error",s=>{"ECONNREFUSED"===s.code?o(new Error(`Cannot connect to server at ${this.apiUrl} — is it running?`)):o(s)}),c&&u.write(c),u.end()})}async login(s,e){const t={email:s,password:e,machineId:os.hostname()},n=await this._request("POST","/thinknsh/login",t,{"x-email":s,"x-password":e,"x-machine-id":os.hostname()});return this._saveSession({token:n.shellToken,shellSessionId:n.shellSessionId,user:{_id:n._id,userId:n._id,email:n.email,name:n.name,userType:n.userType||n.model||"User"},timestamp:(new Date).toISOString()}),{token:n.shellToken,user:this.getUser()}}async logEvent(s,e=null,t={}){const n=this.session?.shellSessionId;n&&await this._request("POST","/thinknsh/session/event",{sessionId:n,type:s,roomId:e,meta:t}).catch(()=>{})}startHeartbeat(s=6e4){this._heartbeatTimer||(this._heartbeatTimer=setInterval(()=>{const s=this.session?.shellSessionId;s&&this._request("POST","/thinknsh/session/event",{sessionId:s,type:"heartbeat",meta:{ts:(new Date).toISOString()}}).catch(()=>{})},s))}stopHeartbeat(){this._heartbeatTimer&&(clearInterval(this._heartbeatTimer),this._heartbeatTimer=null)}async logout(){const s=this.session?.shellSessionId;return s&&await this._request("POST","/thinknsh/session/end",{sessionId:s}).catch(()=>{}),this.stopHeartbeat(),this._clearSession(),{success:!0}}async whoami(){if(!this.isAuthenticated())throw new Error("Not logged in");return this.getUser()}async joinRoom(s,e){if(!s)throw new Error("roomId is required");const t=await this._request("POST",`/thinknsh/rooms/${s}/join`,e?{password:e}:null);return{room:t.room||t,participants:t.participants||t.members||[],recentMessages:t.recentMessages||t.history||[]}}async getRooms(){return this._request("GET","/thinknsh/rooms")}async mytasks(s,e){if(!s)throw new Error("roomId is required");const t=this.getUser();let n=`/thinknsh/tasks?roomId=${encodeURIComponent(s)}`;return t?._id&&(n+=`&userId=${encodeURIComponent(t._id)}`),e&&(n+=`&n=${e}`),this._request("GET",n)}async request(s,e,t={}){let n=e;if(t.params&&Object.keys(t.params).length){const s=new URLSearchParams(t.params).toString();n+=(n.includes("?")?"&":"?")+s}return this._request(s.toUpperCase(),n,t.body||null)}}module.exports=ThinkNCollabAPI;
|
|
@@ -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 crypto=require("crypto"),path=require("path"),os=require("os"),fs=require("fs-extra"),ALGO="aes-256-gcm",IV_LENGTH=12,TAG_LENGTH=16,PBKDF2_ITERATIONS=1e5;class E2EEVault{constructor(){this.keyCache=new Map,this.seedIdCache=new Map,this.streamCounters=new Map,this.vaultDir=path.join(os.homedir(),".tnc","vault"),this.roomsKeyDir=path.join(this.vaultDir,"rooms"),this.knownPeersFile=path.join(this.vaultDir,"known_peers.json");try{fs.ensureDirSync(this.roomsKeyDir,{mode:448}),fs.existsSync(this.knownPeersFile)||fs.writeJsonSync(this.knownPeersFile,{},{spaces:2,mode:384})}catch(e){}}generateRandomRoomKey(){return crypto.randomBytes(32)}deriveKeyFromPassphrase(e,t){const r=Buffer.from(`tnc:room:pbkdf2:salt:${t}:v2`,"utf8");return crypto.pbkdf2Sync(e,r,1e5,32,"sha256")}getRoomKey(e,t=null){if(this.keyCache.has(`${e}:active`))return this.keyCache.get(`${e}:active`);const r=path.join(this.roomsKeyDir,`${e.replace(/[^a-zA-Z0-9_-]/g,"_")}.key`);if(fs.existsSync(r))try{const t=fs.readFileSync(r,"utf8").trim();if(64===t.length){const r=Buffer.from(t,"hex");return this.setRawRoomKey(e,r),r}}catch(e){}if(t){const r=this.deriveKeyFromPassphrase(t,e);return this.setRawRoomKey(e,r),r}const s=this.generateRandomRoomKey();return this.saveRoomKey(e,s),s}saveRoomKey(e,t){this.setRawRoomKey(e,t);try{const r=path.join(this.roomsKeyDir,`${e.replace(/[^a-zA-Z0-9_-]/g,"_")}.key`);fs.writeFileSync(r,t.toString("hex"),{mode:384});try{fs.chmodSync(r,384)}catch(e){}}catch(e){}}rotateRoomKey(e){const t=path.join(this.roomsKeyDir,`${e.replace(/[^a-zA-Z0-9_-]/g,"_")}.key`);if(fs.existsSync(t))try{const r=fs.readFileSync(t,"utf8").trim(),s=path.join(this.roomsKeyDir,"archive");fs.ensureDirSync(s,{mode:448});const n=path.join(s,`${e}_${Date.now()}.key`);fs.writeFileSync(n,r,{mode:384})}catch(e){}const r=this.generateRandomRoomKey();return this.saveRoomKey(e,r),r}getKnownPeers(){try{return fs.existsSync(this.knownPeersFile)?fs.readJsonSync(this.knownPeersFile):{}}catch(e){return{}}}markPeerVerified(e){try{const t=this.getKnownPeers();return!!t[e]&&(t[e].verified=!0,t[e].verifiedAt=(new Date).toISOString(),fs.writeJsonSync(this.knownPeersFile,t,{spaces:2,mode:384}),!0)}catch(e){return!1}}setRawRoomKey(e,t){const r=Buffer.isBuffer(t)?t:Buffer.from(t);if(32===r.length){this.keyCache.set(`${e}:active`,r);const t=this._fingerprint(r);this.seedIdCache.set(t,r)}}_fingerprint(e){const t=Buffer.isBuffer(e)?e:Buffer.from(String(e),"utf8");return crypto.createHash("sha256").update(t).digest("hex").slice(0,8)}computeSafetyNumber(e,t=null){const r=[Buffer.isBuffer(e)?e:Buffer.from(String(e),"utf8"),t?Buffer.isBuffer(t)?t:Buffer.from(String(t),"utf8"):Buffer.alloc(0)].sort(Buffer.compare),s=crypto.createHash("sha512").update(r[0]).update(r[1]).digest();let n="";for(let e=0;e<24;e+=2)n+=(s.readUInt16BE(e)%1e5).toString().padStart(5,"0");return n.match(/.{1,5}/g).join(" ")}verifyPeerPublicKey(e,t){try{const r=fs.existsSync(this.knownPeersFile)?fs.readJsonSync(this.knownPeersFile):{},s=this._fingerprint(t);return r[e]?r[e].fingerprint===s?{trusted:!0,isNew:!1,fingerprint:s}:{trusted:!1,isNew:!1,warning:"KEY_CHANGED",oldFingerprint:r[e].fingerprint,newFingerprint:s}:(r[e]={fingerprint:s,firstSeen:(new Date).toISOString()},fs.writeJsonSync(this.knownPeersFile,r,{spaces:2,mode:384}),{trusted:!0,isNew:!0,fingerprint:s})}catch(e){return{trusted:!0,isNew:!1}}}_generateStreamNonce(e,t="tnc"){let r=this.streamCounters.get(e)||0n;r+=1n,this.streamCounters.set(e,r);const s=crypto.createHash("sha256").update(t).digest().subarray(0,4),n=Buffer.alloc(12);return s.copy(n,0),n.writeBigUInt64BE(r,4),{nonce:n,seq:Number(r)}}encrypt(e,t,r={}){if(!e||"string"!=typeof e)return e;try{const s=this.getRoomKey(t,r.secretSeed),n=this._fingerprint(s),i=r.nonce||crypto.randomBytes(12),o=crypto.createCipheriv(ALGO,s,i);r.aad&&o.setAAD(Buffer.from(String(r.aad),"utf8"));const c=Buffer.concat([o.update(e,"utf8"),o.final()]),a=o.getAuthTag();return`e2ee:v3:${n}:${Buffer.concat([i,a,c]).toString("base64")}`}catch(t){return e}}decrypt(e,t,r={}){if(!e||"string"!=typeof e||!e.startsWith("e2ee:"))return e;try{let s="",n=null;if(e.startsWith("e2ee:v3:")){const t=e.split(":");n=t[2],s=t.slice(3).join(":")}else s=e.startsWith("e2ee:v1:")?e.slice(8):e.slice(5);const i=Buffer.from(s,"base64");if(i.length<28)return e;const o=i.subarray(0,12),c=i.subarray(12,28),a=i.subarray(28);if(n&&this.seedIdCache.has(n)){const e=this.seedIdCache.get(n),t=this._decryptWithKey(a,e,o,c,r.aad);if(null!==t)return t}const f=this.getRoomKey(t,r.secretSeed),h=this._decryptWithKey(a,f,o,c,r.aad);if(null!==h)return h;const u=[`tnc_room_${t}_v1`,`tnc_vault_room_${t}_master_seed`,`tnc_cicd_room_${t}_pipeline_v1`,t];for(const e of u){const t=crypto.createHash("sha256").update(e,"utf8").digest(),s=this._decryptWithKey(a,t,o,c,r.aad);if(null!==s)return s}return e}catch(t){return e}}_decryptWithKey(e,t,r,s,n=null){try{const i=crypto.createDecipheriv(ALGO,t,r);return i.setAuthTag(s),n&&i.setAAD(Buffer.from(String(n),"utf8")),Buffer.concat([i.update(e),i.final()]).toString("utf8")}catch(e){return null}}encryptFile(e,t,r){return new Promise((s,n)=>{try{const i=this.getRoomKey(r),o=crypto.randomBytes(12),c=crypto.createCipheriv(ALGO,i,o),a=fs.createReadStream(e),f=fs.createWriteStream(t);f.write(o),a.pipe(c),c.on("data",e=>f.write(e)),c.on("end",()=>{const e=c.getAuthTag();f.write(e),f.end()}),f.on("finish",()=>s(t)),f.on("error",n),c.on("error",n),a.on("error",n)}catch(e){n(e)}})}decryptFile(e,t,r){return new Promise((s,n)=>{try{const i=this.getRoomKey(r),o=fs.statSync(e);if(o.size<28)return n(new Error("Encrypted file truncated or corrupted"));const c=Buffer.alloc(12),a=fs.openSync(e,"r");fs.readSync(a,c,0,12,0);const f=Buffer.alloc(16);fs.readSync(a,f,0,16,o.size-16),fs.closeSync(a);const h=crypto.createDecipheriv(ALGO,i,c);h.setAuthTag(f);const u=fs.createReadStream(e,{start:12,end:o.size-16-1}),y=fs.createWriteStream(t);u.pipe(h).pipe(y),y.on("finish",()=>s(t)),y.on("error",n),h.on("error",e=>n(new Error(`E2EE Integrity Check Failed: ${e.message}`))),u.on("error",n)}catch(e){n(e)}})}}module.exports=new E2EEVault;
|
|
@@ -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"),DB_DIR=path.join(os.homedir(),".tnc"),DB_PATH=path.join(DB_DIR,"deps.db"),JSON_FALLBACK_PATH=path.join(DB_DIR,"deps.json");class DepStore{constructor(){fs.mkdirSync(DB_DIR,{recursive:!0}),this.db=null,this.useFallback=!1,this._init()}_init(){try{const a=require("better-sqlite3");this.db=new a(DB_PATH),this.db.pragma("journal_mode = WAL"),this._createTables()}catch(a){this.useFallback=!0,this._loadFallback()}}_createTables(){this.db.exec("\n CREATE TABLE IF NOT EXISTS packages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n name TEXT NOT NULL,\n version TEXT,\n manager TEXT NOT NULL, -- npm | pip | cargo | go | brew | apt | system\n project TEXT, -- project folder name\n status TEXT DEFAULT 'pending', -- pending | installed | failed | removed\n installed_at TEXT,\n updated_at TEXT,\n UNIQUE(name, manager, project)\n );\n\n CREATE TABLE IF NOT EXISTS sandbox_deps (\n sandbox_id TEXT NOT NULL,\n package_id INTEGER NOT NULL,\n linked_at TEXT NOT NULL,\n PRIMARY KEY (sandbox_id, package_id),\n FOREIGN KEY (package_id) REFERENCES packages(id)\n );\n\n CREATE TABLE IF NOT EXISTS install_log (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n package_name TEXT NOT NULL,\n manager TEXT NOT NULL,\n action TEXT NOT NULL, -- install | remove | update | fail\n sandbox_id TEXT,\n project TEXT,\n error TEXT,\n timestamp TEXT NOT NULL\n );\n ")}_loadFallback(){try{fs.existsSync(JSON_FALLBACK_PATH)?this._fallbackData=JSON.parse(fs.readFileSync(JSON_FALLBACK_PATH,"utf8")):this._fallbackData={packages:[],log:[]}}catch(a){this._fallbackData={packages:[],log:[]}}}_saveFallback(){try{fs.writeFileSync(JSON_FALLBACK_PATH,JSON.stringify(this._fallbackData,null,2),"utf8")}catch(a){}}recordDep(a,{version:e=null,manager:t="npm",project:n=null,sandboxId:s=null,status:r="installed"}={}){const l=(new Date).toISOString();if(this.useFallback){const i=this._fallbackData.packages.find(e=>e.name===a&&e.manager===t&&e.project===n);return i?(i.version=e||i.version,i.status=r,i.updated_at=l):this._fallbackData.packages.push({name:a,version:e,manager:t,project:n,sandboxId:s,status:r,installed_at:l,updated_at:l}),void this._saveFallback()}if(this.db.prepare("\n INSERT INTO packages (name, version, manager, project, status, installed_at, updated_at)\n VALUES (@name, @version, @manager, @project, @status, @now, @now)\n ON CONFLICT(name, manager, project) DO UPDATE SET\n version = excluded.version,\n status = excluded.status,\n updated_at = excluded.updated_at\n ").run({name:a,version:e,manager:t,project:n,status:r,now:l}),s)try{const e=this.db.prepare("SELECT id FROM packages WHERE name=? AND manager=? AND project=?").get(a,t,n);e&&this.db.prepare("\n INSERT OR IGNORE INTO sandbox_deps (sandbox_id, package_id, linked_at)\n VALUES (?, ?, ?)\n ").run(s,e.id,l)}catch(a){}}recordFromConfig(a,{sandboxId:e=null}={}){const t=a.project||null,n=a.dependencies||{},s={npm:"npm",python:"pip",rust:"cargo",go:"go",system:"system",cpp:"system"};for(const[a,r]of Object.entries(s)){const s=n[a]||[];for(const a of s){const n=a.match(/^([^@>=<^~]+)[@>=<^~]+(.+)?$/),s=n?n[1].trim():a.trim(),l=n&&n[2]?.trim()||null;this.recordDep(s,{version:l,manager:r,project:t,sandboxId:e,status:"installed"})}}}logAction(a,{manager:e="npm",action:t="install",sandboxId:n=null,project:s=null,error:r=null}={}){const l=(new Date).toISOString();if(this.useFallback)return this._fallbackData.log.push({name:a,manager:e,action:t,sandboxId:n,project:s,error:r,timestamp:l}),void this._saveFallback();this.db.prepare("\n INSERT INTO install_log (package_name, manager, action, sandbox_id, project, error, timestamp)\n VALUES (?, ?, ?, ?, ?, ?, ?)\n ").run(a,e,t,n,s,r,l)}listDeps({manager:a=null,project:e=null,sandboxId:t=null,status:n=null}={}){if(this.useFallback){let t=this._fallbackData.packages;return a&&(t=t.filter(e=>e.manager===a)),e&&(t=t.filter(a=>a.project===e)),n&&(t=t.filter(a=>a.status===n)),t}let s="SELECT p.* FROM packages p";const r=[];t&&(s+=" JOIN sandbox_deps sd ON sd.package_id = p.id AND sd.sandbox_id = ?",r.push(t));const l=[];return a&&(l.push("p.manager = ?"),r.push(a)),e&&(l.push("p.project = ?"),r.push(e)),n&&(l.push("p.status = ?"),r.push(n)),l.length&&(s+=" WHERE "+l.join(" AND ")),s+=" ORDER BY p.manager, p.name",this.db.prepare(s).all(...r)}summary({project:a=null}={}){if(this.useFallback){const e=a?this._fallbackData.packages.filter(e=>e.project===a):this._fallbackData.packages,t={};for(const a of e)t[a.manager]=(t[a.manager]||0)+1;return t}const e=this.db.prepare(`\n SELECT manager, COUNT(*) as count FROM packages\n ${a?"WHERE project = ?":""}\n GROUP BY manager\n `).all(...a?[a]:[]),t={};for(const a of e)t[a.manager]=a.count;return t}removeDep(a,{manager:e="npm",project:t=null}={}){if(this.useFallback){const n=this._fallbackData.packages.find(n=>n.name===a&&n.manager===e&&n.project===t);return n&&(n.status="removed"),void this._saveFallback()}this.db.prepare("\n UPDATE packages SET status = 'removed', updated_at = ?\n WHERE name = ? AND manager = ? AND project = ?\n ").run((new Date).toISOString(),a,e,t)}getLog({limit:a=20,sandboxId:e=null}={}){if(this.useFallback)return this._fallbackData.log.slice(-a).reverse();let t="SELECT * FROM install_log";const n=[];return e&&(t+=" WHERE sandbox_id = ?",n.push(e)),t+=" ORDER BY id DESC LIMIT ?",n.push(a),this.db.prepare(t).all(...n)}purgeProject(a){if(this.useFallback)return this._fallbackData.packages=this._fallbackData.packages.filter(e=>e.project!==a),void this._saveFallback();this.db.prepare("DELETE FROM packages WHERE project = ?").run(a)}close(){if(this.db)try{this.db.close()}catch(a){}}}let _instance=null;function getDepStore(){return _instance||(_instance=new DepStore),_instance}module.exports={DepStore,getDepStore};
|
|
@@ -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"),chalk=require("chalk"),MultiLanguageScanner=require("./multi-lang-scanner");let getDepStore;try{getDepStore=require("./dep-store").getDepStore}catch(e){getDepStore=null}class DependencyTracker{constructor(e=process.cwd()){this.cwd=e,this.ymlPath=path.join(this.cwd,"thinkncollab.yml"),this.scanner=new MultiLanguageScanner(this.cwd)}async interceptCommand(e,n=null){if(!e||"string"!=typeof e)return;const t=e.trim(),i=/^(npm|pnpm|yarn|bun)\s+(i|install|add)\s+/i.test(t),s=/^(pip|pip3|poetry|pipenv|conda)\s+(install|add)\s+/i.test(t),r=/^(g\+\+|clang\+\+|cmake|make|conan|vcpkg|ninja)\s+/i.test(t),o=/^cargo\s+(add|install|build)\s+/i.test(t),c=/^go\s+(get|install|mod)\s+/i.test(t),a=/^(brew|apt|apt-get|apk|pacman)\s+(install|add|-S)\s+/i.test(t);(i||s||r||o||c||a)&&(console.log(chalk.dim(`\n [TNC:AutoDep] Intercepting multi-language dependency command: "${t}"`)),await this.scanAndRecord(n))}async scanAndRecord(e=null){try{const n=this.loadConfig(),t=await this.scanner.scan();if(n.runtime.primary=t.primaryRuntime,t.runtimeVersion&&(n.runtime.version=t.runtimeVersion),n.runtime.runtimes=Array.from(t.runtimes),n.dependencies={npm:t.dependencies.npm||[],python:t.dependencies.python||[],cpp:t.dependencies.cpp||[],rust:t.dependencies.rust||[],go:t.dependencies.go||[],system:t.dependencies.system||[]},t.scripts&&(n.scripts={...n.scripts,...t.scripts}),n.addons=Array.from(new Set([...n.addons||[],...Array.from(t.addons||[])])),t.runtimes.has("cpp")&&!n.scripts["build:cpp"]&&(fs.existsSync(path.join(this.cwd,"CMakeLists.txt"))?n.scripts["build:cpp"]="cmake -B build && cmake --build build":fs.existsSync(path.join(this.cwd,"Makefile"))&&(n.scripts["build:cpp"]="make")),this.saveConfig(n),console.log(chalk.green(`[OK] [TNC:AutoDep] Auto-recorded multi-language dependencies to ${chalk.bold("thinkncollab.yml")}`)),getDepStore)try{getDepStore().recordFromConfig(n,{sandboxId:e?.activeSandbox?.id||null}),console.log(chalk.green("[OK] [TNC:AutoDep] Synced dependencies to persistent SQLite DB (~/.tnc/deps.db)"))}catch(e){}if(e?.ws?.isConnected())try{e.ws.socket.emit("sandbox:dependency-sync",{project:n.project,runtimes:n.runtime.runtimes,dependencies:n.dependencies,scripts:n.scripts,timestamp:(new Date).toISOString()})}catch(e){}return n}catch(e){console.warn(chalk.yellow(`[WARN] [TNC:AutoDep] Error recording dependencies: ${e.message}`))}}loadConfig(){if(fs.existsSync(this.ymlPath))try{const e=fs.readFileSync(this.ymlPath,"utf8");return this.parseSimpleYaml(e)}catch(e){}return{version:"1.0",project:path.basename(this.cwd).toLowerCase().replace(/[^a-z0-9_-]/g,"-"),runtime:{primary:"node",runtimes:["node"]},dependencies:{npm:[],python:[],cpp:[],rust:[],go:[],system:[]},addons:[],scripts:{dev:"npm run dev",start:"npm start",test:"npm test"},environment:{PORT:3e3,NODE_ENV:"development"},execution:{isolation:"auto",memory_limit:"2g"}}}saveConfig(e){const n=this.stringifySimpleYaml(e);fs.writeFileSync(this.ymlPath,n,"utf8")}parseSimpleYaml(e){const n={version:"1.0",project:path.basename(this.cwd),runtime:{primary:"node",runtimes:["node"]},dependencies:{npm:[],python:[],cpp:[],rust:[],go:[],system:[]},addons:[],scripts:{},environment:{},execution:{isolation:"auto",memory_limit:"2g"}};let t=null,i=null;const s=e.split("\n");for(const e of s){const s=e.trim();if(s&&!s.startsWith("#"))if("dependencies:"!==s)if("addons:"!==s)if("scripts:"!==s)if("environment:"!==s)if("runtime:"!==s)if("execution:"!==s){if("dependencies"===t)s.endsWith(":")?(i=s.replace(":",""),n.dependencies[i]||(n.dependencies[i]=[])):s.startsWith("-")&&i&&n.dependencies[i].push(s.replace(/^-\s*/,"").replace(/["']/g,""));else if("addons"===t)s.startsWith("-")&&n.addons.push(s.replace(/^-\s*/,"").replace(/["']/g,""));else if("scripts"===t){const e=s.split(":");if(e.length>=2){const t=e[0].trim(),i=e.slice(1).join(":").trim().replace(/["']/g,"");n.scripts[t]=i}}else if("environment"===t){const e=s.split(":");if(e.length>=2){const t=e[0].trim(),i=e.slice(1).join(":").trim().replace(/["']/g,"");n.environment[t]=i}}else if("runtime"===t){const e=s.split(":");if(e.length>=2){const t=e[0].trim(),i=e.slice(1).join(":").trim().replace(/["']/g,"");"primary"!==t&&"type"!==t||(n.runtime.primary=i),"version"===t&&(n.runtime.version=i)}}}else t="execution",i=null;else t="runtime",i=null;else t="environment",i=null;else t="scripts",i=null;else t="addons",i=null;else t="dependencies",i=null}return n}stringifySimpleYaml(e){let n=`# ThinkNCollab Unified Room Execution Blueprint\n# Auto-generated & synced in real-time\nversion: "${e.version||"1.0"}"\nproject: "${e.project}"\n\n`;n+=`runtime:\n primary: "${e.runtime?.primary||"node"}"\n`,e.runtime?.version&&(n+=` version: "${e.runtime.version}"\n`),e.runtime?.runtimes?.length>0&&(n+=" runtimes:\n",e.runtime.runtimes.forEach(e=>n+=` - "${e}"\n`)),n+="\n",e.addons?.length>0&&(n+="addons:\n",e.addons.forEach(e=>n+=` - "${e}"\n`),n+="\n"),n+="dependencies:\n";const t=["npm","python","cpp","rust","go","system"];for(const i of t)e.dependencies?.[i]?.length>0&&(n+=` ${i}:\n`,e.dependencies[i].forEach(e=>n+=` - "${e}"\n`));n+="\nscripts:\n";for(const[t,i]of Object.entries(e.scripts||{}))n+=` ${t}: "${i}"\n`;n+="\nenvironment:\n";for(const[t,i]of Object.entries(e.environment||{}))n+=` ${t}: "${i}"\n`;return n+=`\nexecution:\n isolation: "${e.execution?.isolation||"auto"}"\n memory_limit: "${e.execution?.memory_limit||"2g"}"\n`,n}}module.exports=DependencyTracker;
|
|
@@ -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 cp=require("child_process"),vm=require("vm"),path=require("path"),fs=require("fs-extra"),chalk=require("chalk"),{AsyncLocalStorage}=require("async_hooks"),activeVUStore=new AsyncLocalStorage;class CheckExpression{constructor(t,e=[]){this.target=t,this.args=e,this.ops=[],this.invert=!1,this.msg=null,this.storeName=null}equal(t){return this.ops.push({op:"eq",value:t}),this}great(t){return this.ops.push({op:"gt",value:t}),this}less(t){return this.ops.push({op:"lt",value:t}),this}contains(t){return this.ops.push({op:"contains",value:t}),this}isContained(t){return this.ops.push({op:"isContained",value:t}),this}exists(){return this.ops.push({op:"exists"}),this}not(){return this.invert=!this.invert,this}message(t){return this.msg=t,this}store(t){return this.storeName=t,this}}function statusCode(){return new CheckExpression("status")}function text(){return new CheckExpression("text")}function regexp(t,e=0,s=0){return new CheckExpression("regex",[t,e,s])}function cookie(t){return new CheckExpression("cookie",[t])}function header(t){return new CheckExpression("header",[t])}function xPath(t){return new CheckExpression("xpath",[t])}class HttpResult{constructor(t,e,s){this.statusCode=t,this.headers=e,this.body={asText:s,asObject:(()=>{try{return JSON.parse(s)}catch{return{}}})(),asBytes:Array.from(Buffer.from(s))}}}class HttpRequest{constructor(t,e,s,r){this.method=t,this.url=e,this.path=s,this.conn=r,this.headersMap={},this.cookiesMap={},this.queryParams={},this.bodyData=null,this.checks=[]}header(t,e){return this.headersMap[t]=e,this}cookie(t,e){return this.cookiesMap[t]=e,this}query(t,e){return this.queryParams[t]=e,this}body(t){return this.bodyData=t,this}then(t){return("function"==typeof t||t instanceof CheckExpression)&&this.checks.push(t),this}sync(){let t=this.url.replace(/\/$/,"")+"/"+this.path.replace(/^\//,"");const e=Object.keys(this.queryParams);if(e.length>0){const s=e.map(t=>`${encodeURIComponent(t)}=${encodeURIComponent(this.queryParams[t])}`).join("&");t+=(t.includes("?")?"&":"?")+s}try{const e=new URL(t);if("http:"!==e.protocol&&"https:"!==e.protocol)throw new Error(`Forbidden protocol: ${e.protocol}. Only http: and https: are allowed.`)}catch(e){throw new Error(`Invalid or unsafe URL "${t}": ${e.message}`)}const s=["-i","-s","-L","--proto","=http,https","--proto-redir","=http,https","--connect-timeout","10","--max-time","60","-X",this.method];Object.entries(this.headersMap).forEach(([t,e])=>{s.push("-H",`${t}: ${e}`)});const r=Object.keys(this.cookiesMap);if(r.length>0){const t=r.map(t=>`${t}=${this.cookiesMap[t]}`).join("; ");s.push("-b",t)}if(this.bodyData){const t="object"==typeof this.bodyData?JSON.stringify(this.bodyData):String(this.bodyData);s.push("-d",t),this.headersMap["Content-Type"]||this.headersMap["content-type"]||s.push("-H","Content-Type: application/json")}s.push(t);const n=Date.now(),o=cp.spawnSync("curl",s,{encoding:"utf8",maxBuffer:10485760,timeout:65e3}),a=Date.now()-n;if(o.error)throw new Error(`Failed to execute HTTP request: ${o.error.message}`);const i=o.stdout||"",c=i.includes("\r\n\r\n")?"\r\n\r\n":"\n\n",h=i.indexOf(c);let u="",l="";-1!==h?(u=i.slice(0,h),l=i.slice(h+c.length)):u=i;const p=u.split(/\r?\n/),d=(p[0]||"").match(/HTTP\/[\d\.]+\s+(\d+)/),g=d?parseInt(d[1]):0,f={};for(let t=1;t<p.length;t++){const e=p[t],s=e.indexOf(":");if(-1!==s){const t=e.slice(0,s).trim().toLowerCase(),r=e.slice(s+1).trim();f[t]=r}}const m=new HttpResult(g,f,l),w=activeVUStore.getStore();if(w){w.stats.totalRequests++,w.stats.latencies.push(a);let e=!0;const s=[];for(const t of this.checks)if("function"==typeof t)try{t(m)}catch(t){e=!1,s.push(t.message)}else if(t instanceof CheckExpression){const r=evaluateCheck(t,m,w.session);r.passed||(e=!1,s.push(r.reason))}e||(w.stats.failedRequests++,w.stats.failures.push({url:t,method:this.method,failures:s}))}return m}}function evaluateCheck(t,e,s){let r;if("status"===t.target)r=e.statusCode;else if("text"===t.target)r=e.body.asText;else if("regex"===t.target){const[s,n,o]=t.args,a=new RegExp(s),i=e.body.asText.match(a);r=i&&i[n]||""}else if("cookie"===t.target){const[s]=t.args,n=(e.headers["set-cookie"]||"").match(new RegExp(`${s}=([^;]+)`));r=n?n[1]:void 0}else if("header"===t.target){const[s]=t.args;r=e.headers[s.toLowerCase()]}else if("xpath"===t.target){const[s]=t.args;r=e.body.asText.includes(s)?s:""}let n=!0,o="";for(const e of t.ops){const{op:s,value:a}=e;let i=!1;switch(s){case"eq":i=r==a,i||(o=`Expected "${a}", got "${r}"`);break;case"gt":i=Number(r)>Number(a),i||(o=`Expected > ${a}, got ${r}`);break;case"lt":i=Number(r)<Number(a),i||(o=`Expected < ${a}, got ${r}`);break;case"contains":i=String(r).includes(String(a)),i||(o=`Expected to contain "${a}", got "${r}"`);break;case"isContained":i=String(a).includes(String(r)),i||(o=`Expected "${r}" to be contained in "${a}"`);break;case"exists":i=null!=r,i||(o="Expected to exist, but was undefined/null")}if(t.invert&&(i=!i,i||(o=`Inverted check failed: ${o}`)),!i){n=!1;break}}return n&&t.storeName&&(s[t.storeName]=r),!n&&t.msg&&(o=t.msg),{passed:n,reason:o}}class HttpConnection{constructor(t,e){this.url=t,this.options=e}connect(){return this}get(t){return new HttpRequest("GET",this.url,t,this)}put(t){return new HttpRequest("PUT",this.url,t,this)}post(t){return new HttpRequest("POST",this.url,t,this)}patch(t){return new HttpRequest("PATCH",this.url,t,this)}delete(t){return new HttpRequest("DELETE",this.url,t,this)}options(t){return new HttpRequest("OPTIONS",this.url,t,this)}head(t){return new HttpRequest("HEAD",this.url,t,this)}}function http(t,e){return new HttpConnection(t,e)}function http2(t,e){return new HttpConnection(t,e)}function websocket(t,e){return{connect(){return this},sendText(t){},sendArray(t){},receiveText:()=>"",receiveArray:()=>[]}}function swagger(t,e){return{show(){console.log("Swagger definition loaded from "+t)}}}class StageClass{constructor(t){this.runner=t}async run(t,e,s){await this.runner.runStage(t,e,s)}}class GroupClass{run(t,e,s){return{scenario:t,users:e,profile:s}}}class GeekLoadRunner{constructor(){this.stats={totalRequests:0,failedRequests:0,latencies:[],failures:[],errors:[],stopped:!1},this.results=null,this.completionPromise=new Promise(t=>{this.resolveCompletion=t}),this.stageStarted=!1}async pause(t,e){const s=t=>{const e=String(t).match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)return 1e3;const s=parseFloat(e[1]),r=e[2]||"ms";return"s"===r?1e3*s:"m"===r?60*s*1e3:"h"===r?60*s*60*1e3:s},r=s(t),n=e?s(e):r,o=r+Math.random()*(n-r);return new Promise(t=>setTimeout(t,o))}async runStage(t,e,s){this.stageStarted=!0,console.log(chalk.cyan(`\n Starting Stage: "${t}"`)),console.log(chalk.dim(` Total Virtual Users: ${e}\n`));const r=Date.now(),n=[];for(const t of s){const{scenario:e,users:s,profile:r}=t,o=this.parseDuration(r);console.log(chalk.dim(` Spawning Group scenario... [VUs: ${s}, Duration: ${r}]`));for(let t=0;t<s;t++)n.push(this.runVU(e,o,t+1))}await Promise.all(n);const o=(Date.now()-r)/1e3,a=this.stats.latencies.sort((t,e)=>t-e),i=this.stats.totalRequests,c=this.stats.failedRequests,h=Math.floor(.95*a.length),u=a.length>0?a[h]:0,l=Math.floor(.99*a.length),p=a.length>0?a[l]:0,d=a.length>0?Math.round(a.reduce((t,e)=>t+e,0)/a.length):0,g=o>0?parseFloat((i/o).toFixed(2)):0,f=i>0?parseFloat((c/i).toFixed(4)):0;this.results={virtual_users:e,duration_sec:parseFloat(o.toFixed(2)),p95_latency_ms:u,p99_latency_ms:p,avg_latency_ms:d,failure_rate:f,requests_per_sec:g,total_requests:i,failed_requests:c,failures:this.stats.failures,errors:this.stats.errors},this.resolveCompletion(this.results)}parseDuration(t){const e=String(t).match(/^(\d+(?:\.\d+)?)\s*(ms|s|m|h)?$/);if(!e)return 5e3;const s=parseFloat(e[1]),r=e[2]||"ms";return"s"===r?1e3*s:"m"===r?60*s*1e3:"h"===r?60*s*60*1e3:s}async runVU(t,e,s){const r=Date.now()+e,n={};for(;Date.now()<r&&!this.stats.stopped;)try{await activeVUStore.run({stats:this.stats,session:n,vuId:s},async()=>{if("function"!=typeof t)throw new Error(`Scenario is not an executable function: ${t}`);await t()})}catch(t){this.stats.errors.push({vuId:s,time:Date.now(),message:t.message}),await new Promise(t=>setTimeout(t,500))}}}function transpile(t){let e=t;return e=e.replace(/(?<!async\s+)function\s+([\w\$]+)\s*\(/g,"async function $1("),e=e.replace(/(?<!async\s+)([\w\$]+|\([^\)]*\))\s*=>/g,"async $1 =>"),e=e.replace(/(?<!await\s+)([\w\$\(\)\.\'\"\[\]\-]+)\.sync\(\)/g,"await $1.sync()"),e=e.replace(/(?<!await\s+)pause\(/g,"await pause("),e=e.replace(/await\s+await\s+/g,"await "),e}async function runLoadTest(t){if(!fs.existsSync(t))throw new Error(`Load test script not found: ${t}`);const e=fs.readFileSync(t,"utf8"),s=new GeekLoadRunner,r=vm.createContext({Stage:new StageClass(s),Group:new GroupClass,http,http2,websocket,swagger,pause:s.pause.bind(s),statusCode,text,regexp,cookie,header,xPath,Log:{message(t){console.log(chalk.dim(`[Log] ${t}`))},warning(t){console.log(chalk.yellow(`[Warning] ${t}`))},error(t){console.log(chalk.red(`[Error] ${t}`))}},config:{},console,globalThis:{}}),n=`\n(async () => {\n try {\n ${transpile(e)}\n } catch (err) {\n globalThis.__geekload_err = err;\n }\n})()\n`;return new vm.Script(n).runInContext(r),setTimeout(()=>{if(!s.stageStarted){const t=r.globalThis.__geekload_err||new Error("Script execution failed to call Stage.run");s.resolveCompletion(Promise.reject(t))}},1e3),s.completionPromise}module.exports={runLoadTest,transpile};
|
|
@@ -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"),path=require("path"),{spawn}=require("child_process"),crypto=require("crypto"),SAFE_GIT_URL=/^(https:\/\/|ssh:\/\/|git@)[A-Za-z0-9._~:\/@+-]+$/,SAFE_GIT_BRANCH=/^[A-Za-z0-9._\/-]+$/;function gitCloneShallow(e,t,n,s){const{execFileSync:o}=require("child_process");if("string"!=typeof e||!SAFE_GIT_URL.test(e))throw new Error(`Refusing to clone unsafe repository URL: ${String(e).slice(0,120)}`);if("string"!=typeof t||!SAFE_GIT_BRANCH.test(t)||t.startsWith("-")||t.includes(".."))throw new Error(`Refusing to clone unsafe branch name: ${String(t).slice(0,120)}`);o("git",["clone","--depth=1","--branch",t,"--",e,n],{stdio:s})}function getFetch(){return globalThis.fetch||require("node-fetch")}function parseWorkflow(e){const t=e.split("\n");let n="TNC Local CI",s=[],o="";for(let e=0;e<t.length;e++){const r=t[e].trim();if(!(!r||r.startsWith("#")||r.startsWith("on:")||r.startsWith("jobs:")||r.startsWith("build:")||r.startsWith("runs-on:")))if(!r.startsWith("name:")||o)if(r.startsWith("- name:"))o=r.slice(7).trim().replace(/['"]/g,"");else if(r.startsWith("name:")&&""===o)o=r.slice(5).trim().replace(/['"]/g,"");else{if(r.startsWith("- uses:")||r.startsWith("uses:")){const e=r.startsWith("- uses:")?"- uses:":"uses:",t=r.slice(e.length).trim().replace(/['"]/g,"");let n="";n=t.includes("checkout")?'if command -v git >/dev/null 2>&1 && [ -d .git ]; then echo "[OK] GitHub checkout complete. Commit: $(git rev-parse --short HEAD 2>/dev/null || echo "ready")"; else echo "[OK] GitHub checkout complete. Workspace prepared."; fi':t.includes("auto-build")?'if [ -f package.json ]; then npm run build --if-present; elif [ -f requirements.txt ]; then python3 -m compileall -q -x "venv|.git|__pycache__" .; else echo "Build check complete."; fi':t.includes("secret-scan")?"node -e \"const fs=require('fs'),path=require('path');let found=0;function scan(d){for(const f of fs.readdirSync(d,{withFileTypes:true})){if(f.name.startsWith('.git')||f.name==='node_modules')continue;const p=path.join(d,f.name);if(f.isDirectory())scan(p);else if(/\\.(js|ts|py|env|json|yml|yaml|sh)$/.test(f.name)){try{const c=fs.readFileSync(p,'utf8');if(/BEGIN (RSA|EC|DSA|OPENSSH) PRIVATE KEY|AKIA[0-9A-Z]{16}|ghp_[0-9a-zA-Z]{36}/.test(c)){console.error('[FAIL] Potential secret detected in: '+p);found++;}}catch(_){}}}}try{scan('.');if(found>0){process.exit(1);}else{console.log('[OK] Secret scan passed: zero leaked private keys or API tokens detected.');}}catch(e){console.warn('Scan notice:',e.message);}\"":t.includes("security-audit")?'if [ -f package.json ]; then npm audit --audit-level=critical --no-write 2>/dev/null || true; else echo "Security audit passed."; fi':t.includes("code-quality")?'if [ -f package.json ] && grep -q \'"lint"\' package.json; then npm run lint; elif [ -f requirements.txt ] && command -v flake8 >/dev/null 2>&1; then flake8 .; else echo "Code quality check passed."; fi':`echo "Executing action ${t}"`,s.push({name:o||t,run:n,uses:t}),o="";continue}if(r.startsWith("continue-on-error:")){"true"===r.slice(18).trim().toLowerCase()&&s.length>0&&(s[s.length-1].continueOnError=!0);continue}if(r.startsWith("- run:")||r.startsWith("run:")){const n=r.startsWith("- run:")?"- run:":"run:";let i=r.slice(n.length).trim();if("|"===i){const n=[];let s=e+1;for(;s<t.length;){const e=t[s],o=e.match(/^(\s+)(.*)/);if(!(o&&o[1].length>=2))break;n.push(e.trim()),s++}i=n.join("\n"),e=s-1}if(i){let n=!1;e+1<t.length&&t[e+1].trim().startsWith("continue-on-error: true")&&(n=!0),s.push({name:o||i.slice(0,40),run:i,continueOnError:n}),o=""}}}else n=r.slice(5).trim().replace(/['"]/g,"")}return{workflowName:n,steps:s}}function resolveExecutionEngine(e,t){const{execSync:n}=require("child_process");"darwin"!==process.platform||process.env.PATH?.includes("/opt/homebrew/bin")||(process.env.PATH=`/opt/homebrew/bin:${process.env.PATH||""}`);let s=e.replace(/\bpython\b/g,"python3").replace(/\bpip install\b/g,"pip install").replace(/\bpip3 install\b/g,"pip install").replace(/\bnpm ci\b/g,"npm install --legacy-peer-deps").replace(/\bnpm install\b(?!\s+--legacy-peer-deps)/g,"npm install --legacy-peer-deps");if((s.trim().startsWith("npm ")||s.includes(" npm "))&&(s=`if command -v npm >/dev/null 2>&1 && [ -f package.json ]; then ${s}; else echo "ℹ️ npm or package.json not present — skipping npm step"; fi`),s.includes("node -e")&&(s=`if command -v node >/dev/null 2>&1; then ${s}; else echo "[OK] Step check completed."; fi`),!s.trim())return{shell:"/bin/sh",args:["-c","true"],engineName:"Probe"};const o="win32"===process.platform;if((()=>{try{return n("node --version",{stdio:"ignore"}),!0}catch{return!1}})()){const e=s.replace(/python3 -m pip install(?! --break)/g,"python3 -m pip install --break-system-packages").replace(/pip install(?! --break)/g,"python3 -m pip install --break-system-packages");return{shell:o?"cmd.exe":fs.existsSync("/bin/bash")?"/bin/bash":"/bin/sh",args:o?["/s","/c",e]:["-c",e],engineName:"Native Developer Host"}}if("linux"===process.platform){let e=!1;try{n("which unshare",{stdio:"ignore"}),e=!0}catch(e){}if(e)return{shell:"/bin/sh",args:["-c",`unshare -m -p -u -f /bin/sh -c 'cd "${t}" && ${s.replace(/'/g,"'\\''")}'`],engineName:"TNC Native Kernel Sandbox (Docker-Free)"}}if(o){let e=!1;try{n("wsl --list",{stdio:"ignore"}),e=!0}catch(e){}if(e)return{shell:"cmd.exe",args:["/s","/c",`wsl -d Ubuntu bash -c "cd \\"${t.replace(/^([A-Za-z]):/,(e,t)=>`/mnt/${t.toLowerCase()}`).replace(/\\/g,"/")}\\" && ${s.replace(/"/g,'\\"')}"`],engineName:"Ubuntu Linux (WSL2 Engine)"}}if("darwin"===process.platform){let e=null;if(require("fs").existsSync("/opt/homebrew/bin/limactl"))e="/opt/homebrew/bin/limactl";else try{n("limactl --version",{stdio:"ignore"}),e="limactl"}catch(e){}if(e)try{const o=n(`${e} list --json 2>/dev/null`,{encoding:"utf8"}).trim().split("\n").map(e=>{try{return JSON.parse(e)}catch{return null}}).filter(Boolean);let r=o.find(e=>"ubuntu"===e.name||e.name.includes("ubuntu"))||o[0];if(r){if("Running"!==r.status){console.log(`\n [TNC Engine] Booting Ubuntu VM (${r.name})...`);try{n(`${e} start ${r.name} --tty=false`,{stdio:"ignore"}),console.log(`[OK] Ubuntu VM (${r.name}) booted successfully.\n`)}catch(e){}}const o=t.replace(/\\/g,"/"),i=s.replace(/\\/g,"\\\\").replace(/"/g,'\\"');return{shell:"/bin/bash",args:["-c",`${e} shell ${r.name} nerdctl run --rm -v "${o}:/workspace" -w /workspace ubuntu:22.04 bash -c "${i}"`],engineName:"Ubuntu 22.04 (Ephemeral Isolated Container)"}}}catch(e){}}if(o){let e=!1;try{const{execSync:t}=require("child_process");t("wsl --list --running",{stdio:"ignore"}),e=!0}catch(e){}if(e)return{shell:"cmd.exe",args:["/s","/c",`wsl -d Ubuntu bash -c "${s.replace(/"/g,'\\"')}"`],engineName:"Ubuntu (WSL2)"}}const r=process.platform,i="darwin"===r?" Mac: brew install lima && limactl start template://ubuntu (Optional for VM isolation)":"win32"===r?" Windows: wsl --install -d Ubuntu (then restart)":" Linux: TNC Sandbox uses built-in Linux kernel namespaces (unshare)";console.warn("\n[WARN] [TNC Runner] WARNING: Running on HOST machine — TNC kernel sandbox not available."),console.warn(" To run in isolated environment:"),console.warn(i),console.warn("");const c=s.replace(/python3 -m pip install(?! --break)/g,"python3 -m pip install --break-system-packages").replace(/pip install(?! --break)/g,"python3 -m pip install --break-system-packages");return{shell:o?"cmd.exe":"/bin/sh",args:o?["/s","/c",c]:["-c",c],engineName:"Native Host (Fallback)"}}function runStep(e,t,n){return new Promise(s=>{const o=resolveExecutionEngine(e,t),r=spawn(o.shell,o.args,{cwd:t,env:{...process.env,PUPPETEER_SKIP_DOWNLOAD:"true"}});r.stdout.on("data",e=>n(e.toString())),r.stderr.on("data",e=>n(e.toString())),r.on("close",e=>s(e??0))})}async function dockAvailable(){try{const{DockApi:e}=require("../dock/cli/api");return await(new e).ensureRunning({quiet:!0}),!0}catch(e){return!1}}async function runOnDock(e,t,{onLine:n=()=>{},onStep:s=()=>{}}={}){let o;try{const{DockApi:e}=require("../dock/cli/api");o=new e,await o.ensureRunning({quiet:!0})}catch(e){return{used:!1}}const{runWorkflow:r}=require("../dock/ci/runner"),i=[],c=new Map,l=e=>`${e.job}|${e.leg||""}|${e.step}`,a=e=>`[${e.job}${e.leg?` (${e.leg})`:""}]`,u=await r({projectDir:t,workflowText:e,api:o,onLog:e=>{if("step"===e.type&&"running"===e.status)c.set(l(e),i.length),i.push({name:`${a(e)} ${e.step}`,command:e.step,exitCode:null,passed:!1}),s(c.get(l(e)),"running",`\n ${a(e)} ▶ ${e.step}\n`);else if("step"===e.type&&c.has(l(e))){const t=c.get(l(e));Object.assign(i[t],{exitCode:e.exitCode,passed:"success"===e.status||!!e.ignored}),s(t,"success"===e.status||e.ignored?"passed":"failed",` ${a(e)} ${e.status} (exit ${e.exitCode})\n`)}else if("out"===e.type)n(`${a(e)} ${e.text}`,e.stream,c.get(l(e)));else if("note"===e.type||"leg"===e.type){const t="note"===e.type?e.text:"running"===e.status?`${e.image} · ${e.platform}`:`${e.status}`;n(`${e.job?a(e)+" ":""}${t}`,"system")}}});return{used:!0,passed:u.passed,stepResults:i,summary:u}}async function submitResult(e,t,n,s){const o=getFetch(),r=JSON.stringify(s),i="sha256="+crypto.createHmac("sha256",n).update(r).digest("hex"),c=await o(`${e}/webhooks/tasks/${t}/judge`,{method:"POST",headers:{"Content-Type":"application/json","x-tnc-signature":i},body:r});if(!c.ok){const e=await c.json().catch(()=>({}));throw new Error(e.error||`Webhook ${c.status}`)}return c.json()}async function runLocal({taskId:e,webhookSecret:t,apiUrl:n,repoUrl:s,repoBranch:o,workflowFile:r,chalk:i,shell:c,onLog:l}){const a=process.cwd();console.log(i.cyan(`\n TNC Build triggered for task: ${e}`)),console.log(i.dim(` Local CWD: ${a}`));let u=a,d=null;try{const{execSync:t}=require("child_process");let n=s||null,r=o||"main",c="latest";if(!n)try{n=t("git remote get-url origin",{cwd:a,encoding:"utf8",stdio:"pipe"}).trim(),r=t("git rev-parse --abbrev-ref HEAD",{cwd:a,encoding:"utf8",stdio:"pipe"}).trim(),c=t("git rev-parse --short HEAD",{cwd:a,encoding:"utf8",stdio:"pipe"}).trim()}catch(e){n=null}if(n){const t=require("os");d=path.join(t.homedir(),".tnc","sandboxes",`tnc-runner-${e}-${Date.now()}`),fs.mkdirSync(d,{recursive:!0}),console.log(i.cyan("\n [TNC Checkout] Cloning from GitHub...")),console.log(i.dim(` Remote : ${n}`)),console.log(i.dim(` Branch : ${r} (${c})`)),console.log(i.dim(` Target : ${d}\n`)),gitCloneShallow(n,r,d,"inherit"),u=d;const s=[".tnc/workflows","scripts","routes","controllers","config","signaling","utils","Schema","tests"];for(const e of s){const t=path.join(a,e),n=path.join(d,e);if(fs.existsSync(t))try{fs.mkdirSync(path.dirname(n),{recursive:!0}),fs.cpSync(t,n,{recursive:!0})}catch(e){}}console.log(i.green("\n[OK] GitHub checkout complete — running CI on fresh clone\n"))}}catch(e){console.log(i.yellow(`[WARN] GitHub clone note (${e.message?.split("\n")[0]}) — using local project directory`)),u=a}const p=path.join(u,".tnc","workflows");if(!fs.existsSync(p))return console.log(i.red(`[FAIL] No .tnc/workflows/ directory found in ${u}`)),void console.log(i.yellow(" Create one: mkdir -p .tnc/workflows && touch .tnc/workflows/tnc-ci.yml"));const g=fs.readdirSync(p).filter(e=>e.endsWith(".yml")||e.endsWith(".yaml"));if(!g.length)return void console.log(i.red("[FAIL] No .yml workflow files found in .tnc/workflows/"));const f=r&&g.find(e=>e===r)||g[0],m=fs.readFileSync(path.join(p,f),"utf8");let{workflowName:h,steps:y}=parseWorkflow(m);try{h=require("../dock/ci/workflow").parseWorkflow(m).name||h}catch(e){}if(!y.length)return void console.log(i.red(`[FAIL] No runnable steps found in ${f}`));const w=await dockAvailable()?"dock (a fresh container per job)":resolveExecutionEngine("",u).engineName;console.log(i.cyan(`\n [TNC Actions] Running: "${h}" (${r})`)),console.log(i.green(` [Ubuntu Engine Active] Executing via ${w}`)),console.log(i.dim(` ${y.length} step(s)\n`)),l&&l({workflowName:h,file:r,steps:y.map(e=>({name:e.name})),data:`\n [TNC Actions] Starting Local Workflow: "${h}" (${r})\n Runner Engine: ${w}\n`});const $=[];let k=!0,b="";const S=await runOnDock(m,u,{onLine:(e,t,n)=>{console.log("stderr"===t?i.dim(e):e),b+=e+"\n",l&&l({stepIndex:n??0,state:"running",data:e+"\n"})},onStep:(e,t,n)=>{b+=n,l&&l({stepIndex:e,state:t,data:n})}});S.used&&($.push(...S.stepResults),k=S.passed);for(let e=0;!S.used&&e<y.length;e++){const t=y[e];console.log(i.cyan(` [Step ${e+1}/${y.length}] "${t.name}"`)),console.log(i.gray(`$ ${t.run}\n`));const n=`\n [Step ${e+1}/${y.length}] "${t.name}"\n$ ${t.run}\n`;b+=n,l&&l({stepIndex:e,state:"running",data:n});const s=t=>{process.stdout.write(t),b+=t,l&&l({stepIndex:e,state:"running",data:t})},o=await runStep(t.run,u,s),r=t.continueOnError||/continue-on-error:\s*true/i.test(m)||t.run.includes("npm audit")&&!t.run.includes("--audit-level=critical"),c=0===o||r;if($.push({name:t.name,command:t.run,exitCode:o,passed:c}),0===o){console.log(i.green(`\n[OK] "${t.name}" passed\n`));const n=`\n[OK] Step "${t.name}" completed successfully.\n`;b+=n,l&&l({stepIndex:e,state:"passed",data:n})}else{if(!r){k=!1,console.log(i.red(`\n[FAIL] "${t.name}" failed (exit ${o}). Aborting.\n`));const n=`\n[FAIL] Step "${t.name}" failed with exit code: ${o}. Aborting workflow.\n`;b+=n,l&&l({stepIndex:e,state:"failed",data:n});break}{console.log(i.yellow(`\n[WARN] "${t.name}" advisory check complete (exit ${o}) — continuing pipeline.\n`));const n=`\n[WARN] Step "${t.name}" completed with non-zero exit code ${o} (non-blocking step).\n`;b+=n,l&&l({stepIndex:e,state:"passed",data:n})}}}const x=k?i.green("─".repeat(50)):i.red("─".repeat(50)),v=k?i.bgGreen.black(" PASS "):i.bgRed.white(" FAIL ");console.log(x),console.log(` ${v} ${h}`),console.log(x+"\n");const A={exitCode:k?0:1,stdout:b,stderr:"",output:{workflow:h,status:k?"passed":"failed",stepsCount:y.length,stepsRun:$},json:{workflow:h,status:k?"passed":"failed",stepsCount:y.length,stepsRun:$}};try{console.log(i.dim(" Syncing verdict with TNC...")),await submitResult(n,e,t,A),console.log(i.green(" [OK] Verdict saved — task updated on TNC!\n"))}catch(e){console.log(i.yellow(` [WARN] Sync failed: ${e.message}\n`))}if(c&&c.pushNotification&&c.pushNotification({type:"verdict",passed:k,title:h,reason:k?"TNC Local CI pipeline passed":`Failed at: ${$[$.length-1]?.name}`,taskId:e,at:(new Date).toISOString()}),d&&fs.existsSync(d))try{fs.rmSync(d,{recursive:!0,force:!0}),console.log(i.dim(` Temp clone cleaned up: ${d}\n`))}catch(e){console.log(i.yellow(` [WARN] Could not cleanup temp dir: ${e.message}\n`))}}async function runCicdLocal({runId:e,roomId:t,workflowFile:n,workflowYaml:s,localPath:o,chalk:r,api:i,socket:c}){const l=Date.now(),a=process.cwd(),u=e=>{if(!e||"string"!=typeof e)return e;try{const n=`tnc_cicd_room_${t}_pipeline_v1`,s=crypto.createHash("sha256").update(n).digest(),o=crypto.randomBytes(12),r=crypto.createCipheriv("aes-256-gcm",s,o),i=Buffer.concat([r.update(e,"utf8"),r.final()]),c=r.getAuthTag();return"e2ee:"+Buffer.concat([o,c,i]).toString("base64")}catch(t){return e}},d=(n,s)=>{const o=(new Date).toLocaleTimeString("en-IN",{timeZone:"Asia/Kolkata",hour12:!1}),r=u(s);c&&c.connected&&c.emit("cicd:log",{runId:e,roomId:t,type:n,text:r,timestamp:o})};let p=a,g=null;try{const{execSync:t}=require("child_process");let n=o&&o.startsWith("http")?o.trim():null,s="main";if(!n)try{n=t("git remote get-url origin",{cwd:a,encoding:"utf8",stdio:"pipe"}).trim(),s=t("git rev-parse --abbrev-ref HEAD",{cwd:a,encoding:"utf8",stdio:"pipe"}).trim()}catch(e){n=null}if(n){const t=require("os");g=path.join(t.homedir(),".tnc","sandboxes",`tnc-cicd-${e||Date.now()}-${Date.now()}`),fs.mkdirSync(g,{recursive:!0}),console.log(r.cyan("\n [TNC Checkout] Cloning fresh from GitHub...")),console.log(r.dim(` Remote : ${n}`)),console.log(r.dim(` Branch : ${s}`)),console.log(r.dim(` Target : ${g}\n`)),d("system",` [TNC Checkout] Cloning fresh project from GitHub (${n})...`),gitCloneShallow(n,s,g,"pipe"),p=g,d("system","[OK] Fresh GitHub clone ready.")}}catch(e){d("system",`[WARN] GitHub clone notice: using workspace (${e.message?.split("\n")[0]})`),p=a}console.log(r.cyan(`\n Cloud Dashboard triggered CI/CD workflow: ${n||"universal-polyglot-ci.yml"}`)),console.log(r.dim(` Project: ${p}`));const f=await dockAvailable();f||d("system"," [Ubuntu Engine] Initializing isolated Ubuntu Linux runtime environment...");const m=f?"dock (a fresh container per job)":resolveExecutionEngine("",p).engineName;d("system",` [thinkncollab-shell] Triggered execution for workflow "${n||"universal-polyglot-ci.yml"}"...`),d("system",` Runner Engine: ${m}`),d("system",` Workspace: ${p}`);let h=s||"";if(!h){let e=path.join(p,".tnc","workflows",n||"universal-polyglot-ci.yml");if(!fs.existsSync(e)){const t=path.join(p,".tnc","workflows");if(fs.existsSync(t)){const n=fs.readdirSync(t).filter(e=>e.endsWith(".yml")||e.endsWith(".yaml"));n.length&&(e=path.join(t,n[0]))}}fs.existsSync(e)&&(h=fs.readFileSync(e,"utf8"))}if(!h){const n="[FAIL] Workflow file not found locally or on server";return console.log(r.red(n)),d("error",n),void(c&&c.connected&&c.emit("cicd:status",{runId:e,roomId:t,status:"failed",exitCode:1,durationMs:Date.now()-l}))}let{workflowName:y,steps:w}=parseWorkflow(h);try{y=require("../dock/ci/workflow").parseWorkflow(h).name||y}catch(e){}if(!w.length){const s=`[FAIL] No runnable steps found in ${n}`;return console.log(r.red(s)),d("error",s),void(c&&c.connected&&c.emit("cicd:status",{runId:e,roomId:t,status:"failed",exitCode:1,durationMs:Date.now()-l}))}console.log(r.cyan(`\n [TNC Actions] Running: "${y}" (${n})`)),console.log(r.dim(` ${w.length} step(s)\n`));let $=!0;const k=[];k.push(` [TNC Actions] Starting Local Workflow: "${y}" (${n})`);const b=await runOnDock(h,p,{onLine:(e,t)=>{console.log("stderr"===t?r.dim(e):e),k.push(`[${"system"===t?"system":"stdout"}] ${e}`),d("stderr"===t?"stderr":"system"===t?"system":"stdout",e)},onStep:(e,t,n)=>{k.push(n.trim()),d("failed"===t?"stderr":"system",n.trim())}});b.used&&($=b.passed);for(let e=0;!b.used&&e<w.length;e++){const t=w[e];console.log(r.cyan(` [Step ${e+1}/${w.length}] "${t.name}"`)),console.log(r.gray(`$ ${t.run}\n`)),d("system",`▶ [${e+1}/${w.length}] ${t.name}`),k.push(`\n [Step ${e+1}/${w.length}] "${t.name}"`),k.push(`$ ${t.run}`);const n=e=>{process.stdout.write(e),e.toString().split("\n").forEach(e=>{e.trim()&&(k.push(`[stdout] ${e}`),d("stdout",e))})},s=await runStep(t.run,p,n);if(0!==s){$=!1,console.log(r.red(`\n[FAIL] "${t.name}" failed (exit ${s}). Aborting.\n`)),d("stderr",` [FAIL] Failed (exit ${s})`),k.push(`[FAIL] [Step Failed] Step "${t.name}" exited with status code: ${s}`);break}console.log(r.green(`\n[OK] "${t.name}" passed\n`)),d("system"," [OK] Passed"),k.push(`[OK] [Step Passed] Step "${t.name}" completed successfully`)}const S=Date.now()-l,x=$?r.green("─".repeat(50)):r.red("─".repeat(50)),v=$?r.bgGreen.black(" PASS "):r.bgRed.white(" FAIL ");if(console.log(x),console.log(` ${v} ${y}`),console.log(x+"\n"),$?(d("system","\n Workflow ran successfully! status code: 0"),k.push("\n Workflow ran successfully! status code: 0")):(d("error","\n[FAIL] Workflow failed!"),k.push("\n[FAIL] Workflow failed!")),c&&c.connected&&c.emit("cicd:status",{runId:e,roomId:t,status:$?"passed":"failed",exitCode:$?0:1,durationMs:S}),i)try{console.log(r.dim(" Syncing encrypted CI/CD logs with dashboard..."));const s=k.slice(-300).map(u);await i._request("POST",`/tnccicd/api/save-run/${t}`,{runId:e,workflowFile:n,status:$?"passed":"failed",logs:s,encrypted:!0}).catch(()=>{}),console.log(r.green(" [OK] Encrypted logs synced successfully!\n"))}catch(e){}if(g&&fs.existsSync(g))try{fs.rmSync(g,{recursive:!0,force:!0}),console.log(r.dim(` Isolated sandbox deleted: ${g}\n`)),d("system"," Isolated sandbox deleted & cleaned up.")}catch(e){}}module.exports={runLocal,runCicdLocal,runOnDock};
|
|
@@ -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"),KNOWN_CPP_HEADERS={"boost/":"boost","nlohmann/json.hpp":"nlohmann_json","fmt/":"fmt","opencv2/":"opencv","crow.h":"crow","crow/":"crow","curl/":"curl","openssl/":"openssl","gtest/":"gtest","benchmark/":"benchmark","spdlog/":"spdlog","eigen3/":"eigen","Eigen/":"eigen","grpc/":"grpc","google/protobuf/":"protobuf","yaml-cpp/":"yaml-cpp","sqlite3.h":"sqlite3","zlib.h":"zlib","tbb/":"tbb","poco/":"poco","asio.hpp":"asio","asio/":"asio"},KNOWN_PY_IMPORTS={torch:"torch",torchvision:"torchvision",numpy:"numpy",pandas:"pandas",scipy:"scipy",sklearn:"scikit-learn",cv2:"opencv-python",fastapi:"fastapi",flask:"flask",django:"django",requests:"requests",aiohttp:"aiohttp",uvicorn:"uvicorn",pydantic:"pydantic",sqlalchemy:"sqlalchemy",pytest:"pytest",transformers:"transformers",tensorflow:"tensorflow",keras:"keras",matplotlib:"matplotlib",seaborn:"seaborn",PIL:"pillow",yaml:"pyyaml",dotenv:"python-dotenv",bs4:"beautifulsoup4",openai:"openai",redis:"redis",celery:"celery"},PYTHON_STDLIB=new Set(["os","sys","time","datetime","math","random","json","re","io","collections","itertools","functools","pathlib","typing","subprocess","threading","multiprocessing","asyncio","logging","unittest","http","urllib","socket","ssl","shutil","tempfile","copy","hashlib","base64","struct","queue","select","signal","inspect","traceback","uuid"]);class MultiLanguageScanner{constructor(s=process.cwd()){this.cwd=s}async scan(){const s={runtimes:new Set,primaryRuntime:"node",dependencies:{npm:[],python:[],cpp:[],rust:[],go:[],system:[]},addons:new Set,scripts:{}},e=new Set(["node_modules",".git","build","dist","target","__pycache__",".venv","venv",".cache","bin","obj"]),t=path.join(this.cwd,"package.json");if(fs.existsSync(t))try{const e=fs.readJsonSync(t);if(s.runtimes.add("node"),e.engines?.node?s.runtimeVersion=e.engines.node:fs.existsSync(path.join(this.cwd,".nvmrc"))?s.runtimeVersion=fs.readFileSync(path.join(this.cwd,".nvmrc"),"utf8").trim():fs.existsSync(path.join(this.cwd,".node-version"))?s.runtimeVersion=fs.readFileSync(path.join(this.cwd,".node-version"),"utf8").trim():s.runtimeVersion=process.version,e.dependencies)for(const[t,n]of Object.entries(e.dependencies))s.dependencies.npm.push(t+"@"+n);e.scripts&&(s.scripts={...s.scripts,...e.scripts})}catch(s){}const n=path.join(this.cwd,"requirements.txt");if(fs.existsSync(n)){s.runtimes.add("python");try{const e=fs.readFileSync(n,"utf8").split("\n");for(const t of e){const e=t.trim();!e||e.startsWith("#")||e.startsWith("-r")||s.dependencies.python.push(e)}}catch(s){}}const i=path.join(this.cwd,"pyproject.toml");fs.existsSync(i)&&s.runtimes.add("python");const o=path.join(this.cwd,"CMakeLists.txt");if(fs.existsSync(o)){s.runtimes.add("cpp"),s.dependencies.system.push("cmake");try{const e=fs.readFileSync(o,"utf8").matchAll(/find_package\s*\(\s*([A-Za-z0-9_]+)/gi);for(const t of e){const e=t[1];e&&"threads"!==e.toLowerCase()&&s.dependencies.cpp.push(e.toLowerCase())}}catch(s){}}const r=path.join(this.cwd,"conanfile.txt");if(fs.existsSync(r)){s.runtimes.add("cpp");try{const e=fs.readFileSync(r,"utf8").split("\n");let t=!1;for(const n of e){const e=n.trim();"[requires]"===e?t=!0:e.startsWith("[")?t=!1:t&&e&&s.dependencies.cpp.push(e)}}catch(s){}}const c=path.join(this.cwd,"vcpkg.json");if(fs.existsSync(c)){s.runtimes.add("cpp");try{const e=fs.readJsonSync(c);Array.isArray(e.dependencies)&&e.dependencies.forEach(e=>{const t="string"==typeof e?e:e.name;t&&s.dependencies.cpp.push(t)})}catch(s){}}const a=path.join(this.cwd,"Cargo.toml");if(fs.existsSync(a)){s.runtimes.add("rust");try{const e=fs.readFileSync(a,"utf8").split("\n");let t=!1;for(const n of e){const e=n.trim();if("[dependencies]"===e)t=!0;else if(e.startsWith("["))t=!1;else if(t&&e.includes("=")){const t=e.split("=");s.dependencies.rust.push(t[0].trim()+"@"+t[1].trim().replace(/["']/g,""))}}}catch(s){}}const p=path.join(this.cwd,"go.mod");if(fs.existsSync(p)){s.runtimes.add("go");try{const e=fs.readFileSync(p,"utf8").split("\n");let t=!1;for(const n of e){const e=n.trim();if(e.startsWith("require ("))t=!0;else if(")"===e)t=!1;else if(t&&e){const t=e.split(/\s+/);s.dependencies.go.push(t[0]+"@"+(t[1]||"latest"))}else if(e.startsWith("require ")&&!e.includes("(")){const t=e.replace("require ","").trim().split(/\s+/);s.dependencies.go.push(t[0]+"@"+(t[1]||"latest"))}}}catch(s){}}try{this.scanSourceFiles(this.cwd,s,e,0,3)}catch(s){}for(const e of Object.keys(s.dependencies))s.dependencies[e]=Array.from(new Set(s.dependencies[e]));1===s.runtimes.size?s.primaryRuntime=Array.from(s.runtimes)[0]:s.runtimes.size>1?s.primaryRuntime=s.runtimes.has("node")?"node":s.runtimes.has("python")?"python":"cpp":(s.primaryRuntime="node",s.runtimes.add("node"));const d=s.dependencies.npm||[],h=s.dependencies.python||[];return d.some(s=>s.startsWith("mediasoup@")||s.startsWith("@mediasoup/"))&&s.addons.add("sfu"),(d.some(s=>s.startsWith("ioredis@")||s.startsWith("redis@")||s.startsWith("@socket.io/redis-adapter@"))||h.some(s=>s.startsWith("redis")||s.startsWith("celery")))&&s.addons.add("redis"),d.some(s=>s.startsWith("puppeteer@")||s.startsWith("playwright@"))&&s.addons.add("headless-browser"),(d.some(s=>s.startsWith("@xenova/transformers@"))||h.some(s=>s.startsWith("transformers")||s.startsWith("torch")))&&s.addons.add("ai-vector-engine"),s}scanSourceFiles(s,e,t,n=0,i=3){if(n>i)return;const o=fs.readdirSync(s,{withFileTypes:!0});for(const r of o){if(r.name.startsWith(".")||t.has(r.name))continue;const o=path.join(s,r.name);if(r.isDirectory())this.scanSourceFiles(o,e,t,n+1,i);else if(r.isFile()){const s=path.extname(r.name).toLowerCase();if([".cpp",".hpp",".cc",".cxx",".c",".h"].includes(s)){e.runtimes.add("cpp");try{const s=fs.readFileSync(o,"utf8").matchAll(/#include\s*[<"]([^>"]+)[>"]/g);for(const t of s){const s=t[1];for(const[t,n]of Object.entries(KNOWN_CPP_HEADERS))(s.startsWith(t)||s===t)&&e.dependencies.cpp.push(n)}}catch(s){}}if(".py"===s){e.runtimes.add("python");try{const s=fs.readFileSync(o,"utf8").matchAll(/(?:^|\n)\s*(?:import|from)\s+([A-Za-z0-9_]+)/g);for(const t of s){const s=t[1];!PYTHON_STDLIB.has(s)&&KNOWN_PY_IMPORTS[s]&&e.dependencies.python.push(KNOWN_PY_IMPORTS[s])}}catch(s){}}".rs"===s&&e.runtimes.add("rust"),".go"===s&&e.runtimes.add("go")}}}}module.exports=MultiLanguageScanner;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* @license ThinkNCollab Enterprise & Open Core Ecosystem
|
|
4
|
+
* Copyright (c) 2024-2026 ThinkNCollab Systems Inc. All rights reserved.
|
|
5
|
+
* PROPRIETARY INTELLECTUAL PROPERTY & TRADE SECRETS.
|
|
6
|
+
* UNAUTHORIZED REVERSE ENGINEERING, DECOMPILATION, OR RE-DISTRIBUTION IS STRICTLY PROHIBITED.
|
|
7
|
+
*/
|
|
8
|
+
const net=require("net"),chalk=require("chalk"),{readToken,tokenFile}=require("./notify-auth"),rawPort=process.argv.find(e=>/^\d{4,5}$/.test(e))||process.env.THINKNSH_NOTIFY_PORT||"7379",PORT=parseInt(rawPort,10),HOST="127.0.0.1";function clearScreen(){process.stdout.write("c")}function drawHeader(){const e=process.stdout.columns||80,o="-".repeat(e);console.log(chalk.cyan(o)),console.log(chalk.cyan.bold(" ThinkNCollab -- Live Notifications ".padStart(Math.floor((e+36)/2)))),console.log(chalk.cyan(o)),console.log(chalk.dim(` Port: ${PORT} | Notifications will appear here in real time.`)),console.log(chalk.dim(" Close this window or press Ctrl+C to stop.\n"))}function beep(){process.stdout.write("")}function printNotification(e){const o=(new Date).toLocaleTimeString(),s=chalk.dim(`[${o}]`);switch(e.type){case"task":beep(),console.log(`${s} [TASK] ${chalk.yellow("Task Assigned ->")} ${chalk.white(e.message)}`);break;case"task_reopened":beep(),console.log(`${s} [REOPEN] ${chalk.yellow("Task Reopened ->")} ${chalk.white(e.message.replace(/^[^\w\s]+\s*/,""))}`);break;case"task_completed":beep(),e.message?console.log(`${s} [DONE] ${chalk.green("Task Completed ->")} ${chalk.white(e.message.replace(/^[^\w\s]+\s*/,""))}`):console.log(`${s} [DONE] ${chalk.green(e.userName||"Someone")} completed ${chalk.white(e.taskTitle)}`);break;case"task_progress":beep(),console.log(`${s} [PROGRESS] ${chalk.blue("Task Progress ->")} ${chalk.white(e.message)}`);break;case"task_assigned":beep(),console.log(`${s} [TASK] ${chalk.yellow("Task assigned ->")} ${chalk.white(e.taskTitle)} ${chalk.dim("by")} ${chalk.cyan(e.assignedBy)}`);break;case"task_started":console.log(`${s} [START] ${chalk.green(e.userName)} started working on ${chalk.white(e.taskTitle)}`);break;case"message":beep(),console.log(`${s} [MSG] ${chalk.cyan(e.from)}: ${e.text}`);break;case"userJoined":console.log(`${s} [USER+] ${chalk.green(e.username)} joined`);break;case"userLeft":console.log(`${s} [USER-] ${chalk.yellow(e.username)} left`);break;case"connected":console.log(`${s} ${chalk.green("[OK] Shell connected")}`);break;case"disconnected":console.log(`${s} ${chalk.yellow("[WARN] Shell disconnected")}`);break;case"notification":{beep();const o={success:chalk.green,warning:chalk.yellow,error:chalk.red,info:chalk.blue}[e.level]||chalk.blue;console.log(`${s} [ALERT] ${o(`${e.title}: ${e.message}`)}`);break}case"verdict":beep();{const o=e.passed?chalk.green:chalk.red,l=e.passed?"[PASS]":"[FAIL]",c=e.passed?"PASSED":"FAILED";console.log(`${s} ${l} ${o(`Build Verdict [${c}]`)}: ${chalk.white(e.reason||"")}`)}break;case"typing":beep(),process.stdout.write(`\r${chalk.dim(`[Typing] ${e.username} is typing... `)}`);break;case"stoppedTyping":process.stdout.write("\r"+" ".repeat(40)+"\r");break;default:e.message?console.log(`${s} [Broadcast] ${chalk.white(e.message)}`):console.log(`${s} ${chalk.white(JSON.stringify(e))}`)}}function connect(){clearScreen(),drawHeader(),console.log(chalk.dim(` Connecting to shell on port ${PORT}...`));const e=new net.Socket;let o="";e.connect(PORT,HOST,()=>{const o=readToken(PORT);if(!o)return console.log(chalk.red(` No session token for port ${PORT} (${tokenFile(PORT)}). Is the shell on this port running as you?`)),void e.destroy();e.write(JSON.stringify({token:o})+"\n"),console.log(chalk.green(" Connected to shell!\n"))}),e.on("data",e=>{o+=e.toString();const s=o.split("\n");o=s.pop(),s.forEach(e=>{if(e.trim())try{printNotification(JSON.parse(e))}catch{console.log(chalk.dim(e))}})}),e.on("close",()=>{console.log(chalk.yellow("\n Shell disconnected. Retrying in 3s...")),setTimeout(connect,3e3)}),e.on("error",e=>{"ECONNREFUSED"===e.code?console.log(chalk.dim(` Waiting for shell on port ${PORT}...`)):console.log(chalk.dim(` Connection error: ${e.message}`)),setTimeout(connect,3e3)})}process.on("SIGINT",()=>{console.log(chalk.yellow("\n Notification window closed.")),process.exit(0)}),connect();
|
|
@@ -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 fs=require("fs"),os=require("os"),path=require("path"),crypto=require("crypto"),HANDSHAKE_MS=3e3,stateDir=()=>path.join(os.homedir(),".tnc","state"),tokenFile=e=>path.join(stateDir(),`notify-${Number(e)}.token`);function writeToken(e){const t=crypto.randomBytes(24).toString("hex");fs.mkdirSync(stateDir(),{recursive:!0,mode:448});const r=tokenFile(e);return fs.rmSync(r,{force:!0}),fs.writeFileSync(r,`${t}\n`,{mode:384,flag:"wx"}),t}function readToken(e){if(process.env.THINKNSH_NOTIFY_TOKEN)return process.env.THINKNSH_NOTIFY_TOKEN.trim();try{return fs.readFileSync(tokenFile(e),"utf8").trim()}catch(e){return null}}function removeToken(e){try{fs.rmSync(tokenFile(e),{force:!0})}catch(e){}}function sameToken(e,t){const r=Buffer.from(String(e||"")),n=Buffer.from(String(t||""));return r.length===n.length&&r.length>0&&crypto.timingSafeEqual(r,n)}function guard(e,t,r){let n="";const o=setTimeout(()=>e.destroy(),3e3),i=s=>{if(n+=s.toString("utf8"),n.length>512)return clearTimeout(o),void e.destroy();const c=n.indexOf("\n");if(-1===c)return;clearTimeout(o),e.removeListener("data",i);let u=null;try{u=JSON.parse(n.slice(0,c)).token}catch(e){}sameToken(u,t)?r(e):e.destroy()};e.on("data",i),e.on("error",()=>clearTimeout(o))}module.exports={writeToken,readToken,removeToken,guard,tokenFile};
|
|
@@ -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"),CONFIG_FILE="tncproject",MAGIC=Buffer.from("TNCP"),ROOM_ID_SIZE=24,MIN_SIZE=59;function findConfigFile(o=process.cwd()){let r=path.resolve(o);const t=path.parse(r).root;for(;;){for(const o of["tncproject",".tncproject"]){const t=path.join(r,o);if(fs.existsSync(t))return t}if(r===t||r===os.homedir())break;r=path.dirname(r)}return null}function loadProjectConfig(o=process.cwd()){const r=findConfigFile(o);if(!r)throw new Error("No .tncproject file found in this directory or any parent.\n Download it from: ThinkNCollab → Room → Project Config");const t=fs.readFileSync(r);if(t.length<59)throw new Error("Invalid .tncproject — file too small or corrupted.\n Re-download it from the dashboard.");if(!t.slice(0,4).equals(MAGIC))throw new Error("Not a ThinkNCollab project config file.\n Re-download from the dashboard.");const e=t.readUInt8(4);if(1!==e)throw new Error(`Unsupported .tncproject version (${e}).\n Update thinkncollab-shell: npm update -g thinkncollab-shell`);const n=t.slice(5,29).toString("ascii").trim();if(!/^[a-f0-9]{24}$/i.test(n))throw new Error("Corrupted .tncproject — invalid roomId.\n Re-download it from the dashboard.");return{filePath:r,roomId:n,fileBase64:t.toString("base64")}}function hasProjectConfig(o=process.cwd()){return!!findConfigFile(o)}module.exports={loadProjectConfig,hasProjectConfig,CONFIG_FILE};
|
|
@@ -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"),HOST_ARCH=(()=>{const s=process.arch;return"arm64"===s||"aarch64"===s?"arm64":"amd64"})(),HOST_PLATFORM=process.platform,QEMU_BINARIES={amd64:"qemu-x86_64-static",arm64:"qemu-aarch64-static",arm:"qemu-arm-static",386:"qemu-i386-static",ppc64le:"qemu-ppc64le-static",s390x:"qemu-s390x-static"},BINFMT_MAGIC={amd64:{magic:"\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x3e\\x00",mask:"\\xff\\xff\\xff\\xff\\xff\\xfe\\xfe\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff",offset:0},arm64:{magic:"\\x7fELF\\x02\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\xb7\\x00",mask:"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff",offset:0},arm:{magic:"\\x7fELF\\x01\\x01\\x01\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x02\\x00\\x28\\x00",mask:"\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\x00\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xff\\xfe\\xff\\xff\\xff",offset:0}};class ArchShim{constructor(){this.hostArch=HOST_ARCH,this.platform=HOST_PLATFORM}detectImageArch(s){try{const t=path.join(s,"config.json");if(fs.existsSync(t)){const s=fs.readJsonSync(t),r=s.architecture||s.config?.architecture;if(r)return"amd64"===r||"x86_64"===r?"amd64":"arm64"===r||"aarch64"===r?"arm64":r}}catch(s){}return"amd64"}needsTranslation(s){return s!==this.hostArch}hasRosetta2(){if("darwin"!==this.platform)return!1;try{return execSync("/usr/bin/arch -x86_64 /usr/bin/true 2>/dev/null",{stdio:"pipe"}),!0}catch(s){return!1}}async ensureRosetta2(){if(this.hasRosetta2())return!0;console.log(chalk.yellow(" [Arch] Rosetta 2 not installed. Installing..."));try{return execSync("softwareupdate --install-rosetta --agree-to-license",{stdio:"inherit"}),!0}catch(s){return console.log(chalk.red(" [Arch] Rosetta 2 install failed. Run: softwareupdate --install-rosetta --agree-to-license")),!1}}hasBinfmt(s){const t=`/proc/sys/fs/binfmt_misc/qemu-${s}`;return fs.existsSync(t)}findQEMU(s){const t=QEMU_BINARIES[s];if(!t)return null;const r=[`/usr/bin/${t}`,`/usr/local/bin/${t}`,`/usr/static/${t}`];for(const s of r)if(fs.existsSync(s))return s;return null}registerBinfmt(s){if("darwin"===this.platform||"win32"===this.platform)return{success:!1,reason:"binfmt_misc only for Linux"};const t=this.findQEMU(s);if(!t)return{success:!1,reason:"QEMU binary not found. Install: sudo apt-get install qemu-user-static"};if(this.hasBinfmt(s))return{success:!0,cached:!0};const r=BINFMT_MAGIC[s];if(!r)return{success:!1,reason:`No binfmt magic for arch: ${s}`};try{const e=`:qemu-${s}:M::${r.magic}:${r.mask}:${t}:F`;return execSync(`echo '${e}' > /proc/sys/fs/binfmt_misc/register`,{stdio:"pipe"}),{success:!0}}catch(s){return{success:!1,reason:`binfmt_misc register failed (need root): ${s.message}`}}}async getExecWrapper(s){if(!this.needsTranslation(s))return{wrapper:[],archInfo:`native (${this.hostArch})`,needsTranslation:!1};if(console.log(chalk.yellow(` [Arch] Image: ${s} | Host: ${this.hostArch} → Cross-arch translation needed`)),"darwin"===this.platform&&"arm64"===this.hostArch&&"amd64"===s&&await this.ensureRosetta2())return console.log(chalk.green(" [Arch] Using Rosetta 2 translation ✓")),{wrapper:["/usr/bin/arch","-x86_64"],archInfo:"Rosetta 2 (arm64→x86_64)",needsTranslation:!0};if("darwin"===this.platform&&"amd64"===this.hostArch&&"arm64"===s){const s=this.findQEMU("arm64")||"/opt/homebrew/bin/qemu-aarch64";if(fs.existsSync(s))return console.log(chalk.green(" [Arch] Using QEMU for arm64 on Intel Mac ✓")),{wrapper:[s],archInfo:"QEMU (x86_64→arm64)",needsTranslation:!0};console.log(chalk.yellow(" [Arch] QEMU not found. Install: brew install qemu"))}if("linux"===this.platform){const t=this.registerBinfmt(s);if(t.success){const t=this.findQEMU(s);return console.log(chalk.green(` [Arch] binfmt_misc registered for ${s} ✓`)),{wrapper:t?[t]:[],archInfo:`binfmt_misc + QEMU (${this.hostArch}→${s})`,needsTranslation:!0,requiresBinfmt:!0}}console.log(chalk.yellow(` [Arch] binfmt_misc failed: ${t.reason}`))}return"win32"===this.platform?(console.log(chalk.dim(" [Arch] Windows: cross-arch via WSL2 binfmt_misc")),{wrapper:["wsl","--"],archInfo:`WSL2 binfmt_misc (${s})`,needsTranslation:!0}):(console.log(chalk.yellow(` [Arch] WARNING: No translator found for ${this.hostArch}→${s}. Binary may fail.`)),{wrapper:[],archInfo:`unknown (${s})`,needsTranslation:!0,warned:!0})}injectQEMU(s,t){const r=this.findQEMU(t);if(!r||!fs.existsSync(r))return!1;const e=path.join(s,"usr","bin"),n=path.join(e,path.basename(r));try{return fs.ensureDirSync(e),fs.copyFileSync(r,n),fs.chmodSync(n,493),console.log(chalk.dim(` [Arch] Injected ${path.basename(r)} into rootfs`)),!0}catch(s){return!1}}status(){const s={hostArch:this.hostArch,platform:this.platform,rosetta2:"darwin"===this.platform?this.hasRosetta2():"N/A",binfmt:{}};if("linux"===this.platform)for(const t of["amd64","arm64","arm"])t!==this.hostArch&&(s.binfmt[t]=this.hasBinfmt(t));const t={};for(const[s,r]of Object.entries(QEMU_BINARIES)){const r=this.findQEMU(s);r&&(t[s]=r)}return s.qemu=t,s}}module.exports=ArchShim;
|