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,385 @@
|
|
|
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 PAGE=String.raw`<!doctype html>
|
|
8
|
+
<html lang="en">
|
|
9
|
+
<head>
|
|
10
|
+
<meta charset="utf-8">
|
|
11
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
12
|
+
<title>dock dashboard</title>
|
|
13
|
+
<style>
|
|
14
|
+
:root {
|
|
15
|
+
--bg: #f6f7f9; --panel: #ffffff; --text: #18202b; --muted: #5d6878; --line: #e3e7ed;
|
|
16
|
+
--accent: #3b5bdb; --ok: #2b8a3e; --warn: #c27b00; --bad: #c92a2a; --chip: #eef1f6; --code: #0f1720; --codetext: #d6e2f0;
|
|
17
|
+
}
|
|
18
|
+
@media (prefers-color-scheme: dark) {
|
|
19
|
+
:root:not([data-theme="light"]) {
|
|
20
|
+
--bg: #0f1318; --panel: #171c23; --text: #e3e8ef; --muted: #8d98a8; --line: #26303b;
|
|
21
|
+
--accent: #7c95ff; --ok: #51cf66; --warn: #fcc419; --bad: #ff6b6b; --chip: #212833; --code: #0a0d11; --codetext: #c9d6e6;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
:root[data-theme="dark"] {
|
|
25
|
+
--bg: #0f1318; --panel: #171c23; --text: #e3e8ef; --muted: #8d98a8; --line: #26303b;
|
|
26
|
+
--accent: #7c95ff; --ok: #51cf66; --warn: #fcc419; --bad: #ff6b6b; --chip: #212833; --code: #0a0d11; --codetext: #c9d6e6;
|
|
27
|
+
}
|
|
28
|
+
* { box-sizing: border-box; }
|
|
29
|
+
body { margin: 0; background: var(--bg); color: var(--text); font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; }
|
|
30
|
+
header { display: flex; align-items: center; gap: 12px; padding: 12px 20px; background: var(--panel); border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 2; }
|
|
31
|
+
header h1 { font-size: 16px; margin: 0; letter-spacing: .2px; }
|
|
32
|
+
header .meta { color: var(--muted); font-size: 12px; }
|
|
33
|
+
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); display: inline-block; }
|
|
34
|
+
.dot.on { background: var(--ok); }
|
|
35
|
+
.spacer { flex: 1; }
|
|
36
|
+
nav { display: flex; gap: 4px; padding: 10px 20px 0; flex-wrap: wrap; }
|
|
37
|
+
nav button { border: 0; background: none; color: var(--muted); padding: 8px 12px; border-radius: 8px 8px 0 0; cursor: pointer; font: inherit; }
|
|
38
|
+
nav button.active { color: var(--text); background: var(--panel); border: 1px solid var(--line); border-bottom-color: var(--panel); }
|
|
39
|
+
main { padding: 0 20px 24px; }
|
|
40
|
+
.card { background: var(--panel); border: 1px solid var(--line); border-radius: 0 10px 10px 10px; padding: 14px; overflow-x: auto; }
|
|
41
|
+
.toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; flex-wrap: wrap; }
|
|
42
|
+
.toolbar label { color: var(--muted); font-size: 13px; display: flex; gap: 6px; align-items: center; }
|
|
43
|
+
table { width: 100%; border-collapse: collapse; }
|
|
44
|
+
th, td { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--line); vertical-align: middle; white-space: nowrap; }
|
|
45
|
+
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: .4px; }
|
|
46
|
+
tr.row { cursor: pointer; }
|
|
47
|
+
tr.row:hover td { background: var(--chip); }
|
|
48
|
+
tr.sel td { background: var(--chip); }
|
|
49
|
+
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }
|
|
50
|
+
.badge { display: inline-block; padding: 2px 8px; border-radius: 999px; font-size: 12px; background: var(--chip); color: var(--muted); }
|
|
51
|
+
.badge.running { color: var(--ok); } .badge.exited, .badge.dead { color: var(--bad); } .badge.paused, .badge.created, .badge.restarting { color: var(--warn); }
|
|
52
|
+
.muted { color: var(--muted); }
|
|
53
|
+
button.btn { font: inherit; font-size: 12.5px; border: 1px solid var(--line); background: var(--panel); color: var(--text); border-radius: 7px; padding: 4px 9px; cursor: pointer; }
|
|
54
|
+
button.btn:hover { border-color: var(--accent); }
|
|
55
|
+
button.btn.danger:hover { border-color: var(--bad); color: var(--bad); }
|
|
56
|
+
button.btn:disabled { opacity: .45; cursor: default; }
|
|
57
|
+
.actions { display: flex; gap: 6px; }
|
|
58
|
+
.detail { margin-top: 14px; border-top: 1px solid var(--line); padding-top: 14px; display: grid; grid-template-columns: minmax(0, 1fr) 300px; gap: 14px; }
|
|
59
|
+
@media (max-width: 860px) { .detail { grid-template-columns: 1fr; } }
|
|
60
|
+
pre.logs { background: var(--code); color: var(--codetext); margin: 0; padding: 12px; border-radius: 8px; height: 360px; overflow: auto; white-space: pre-wrap; word-break: break-all; font: 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace; }
|
|
61
|
+
.stats { display: grid; gap: 10px; }
|
|
62
|
+
.stat { background: var(--chip); border-radius: 8px; padding: 10px 12px; }
|
|
63
|
+
.stat .k { color: var(--muted); font-size: 12px; } .stat .v { font-size: 18px; font-weight: 600; margin-top: 2px; }
|
|
64
|
+
.bar { height: 6px; background: var(--line); border-radius: 3px; overflow: hidden; margin-top: 6px; } .bar > i { display: block; height: 100%; background: var(--accent); width: 0; transition: width .4s; }
|
|
65
|
+
dl { display: grid; grid-template-columns: 110px minmax(0,1fr); gap: 4px 10px; margin: 0; font-size: 13px; } dt { color: var(--muted); } dd { margin: 0; overflow-wrap: anywhere; }
|
|
66
|
+
.grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 10px; }
|
|
67
|
+
.empty { color: var(--muted); padding: 24px; text-align: center; }
|
|
68
|
+
#toast { position: fixed; right: 16px; bottom: 16px; max-width: 420px; display: grid; gap: 8px; z-index: 5; }
|
|
69
|
+
#toast div { background: var(--panel); border: 1px solid var(--bad); color: var(--text); padding: 10px 12px; border-radius: 8px; box-shadow: 0 4px 16px rgba(0,0,0,.15); }
|
|
70
|
+
#locked { padding: 60px 20px; text-align: center; color: var(--muted); }
|
|
71
|
+
</style>
|
|
72
|
+
</head>
|
|
73
|
+
<body>
|
|
74
|
+
<header>
|
|
75
|
+
<span class="dot" id="dot"></span>
|
|
76
|
+
<h1>dock</h1>
|
|
77
|
+
<span class="meta" id="meta">connecting…</span>
|
|
78
|
+
<span class="spacer"></span>
|
|
79
|
+
<button class="btn" id="theme" title="Switch light / dark">◐</button>
|
|
80
|
+
</header>
|
|
81
|
+
<div id="locked" hidden>This page needs the link printed by <span class="mono">thinknsh dock ui</span> (it carries the session token).</div>
|
|
82
|
+
<nav id="tabs"></nav>
|
|
83
|
+
<main><div class="card" id="view"></div></main>
|
|
84
|
+
<div id="toast"></div>
|
|
85
|
+
<script>
|
|
86
|
+
(function () {
|
|
87
|
+
'use strict';
|
|
88
|
+
var token = (location.hash.match(/t=([0-9a-f]+)/) || [])[1] || sessionStorage.getItem('tnc-ui-token');
|
|
89
|
+
if (token) { sessionStorage.setItem('tnc-ui-token', token); history.replaceState(null, '', location.pathname); }
|
|
90
|
+
|
|
91
|
+
var root = document.documentElement;
|
|
92
|
+
try { var t = localStorage.getItem('tnc-ui-theme'); if (t) root.setAttribute('data-theme', t); } catch (e) {}
|
|
93
|
+
document.getElementById('theme').onclick = function () {
|
|
94
|
+
var dark = root.getAttribute('data-theme') ? root.getAttribute('data-theme') === 'dark' : matchMedia('(prefers-color-scheme: dark)').matches;
|
|
95
|
+
var next = dark ? 'light' : 'dark';
|
|
96
|
+
root.setAttribute('data-theme', next);
|
|
97
|
+
try { localStorage.setItem('tnc-ui-theme', next); } catch (e) {}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
function el(tag, attrs, kids) {
|
|
101
|
+
var n = document.createElement(tag);
|
|
102
|
+
if (attrs) for (var k in attrs) {
|
|
103
|
+
if (k === 'text') n.textContent = attrs[k];
|
|
104
|
+
else if (k === 'class') n.className = attrs[k];
|
|
105
|
+
else if (k.slice(0, 2) === 'on') n.addEventListener(k.slice(2), attrs[k]);
|
|
106
|
+
else if (attrs[k] !== undefined && attrs[k] !== null && attrs[k] !== false) n.setAttribute(k, attrs[k] === true ? '' : attrs[k]);
|
|
107
|
+
}
|
|
108
|
+
(kids || []).forEach(function (c) { if (c != null) n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c); });
|
|
109
|
+
return n;
|
|
110
|
+
}
|
|
111
|
+
function toast(msg) {
|
|
112
|
+
var box = el('div', { text: msg });
|
|
113
|
+
document.getElementById('toast').appendChild(box);
|
|
114
|
+
setTimeout(function () { box.remove(); }, 6000);
|
|
115
|
+
}
|
|
116
|
+
function api(method, path) {
|
|
117
|
+
return fetch('/api' + path, { method: method, headers: { 'x-tnc-ui': token || '' } }).then(function (r) {
|
|
118
|
+
if (r.status === 204 || r.status === 304) return null;
|
|
119
|
+
return r.text().then(function (t) {
|
|
120
|
+
var body; try { body = t ? JSON.parse(t) : null; } catch (e) { body = t; }
|
|
121
|
+
if (!r.ok) throw new Error((body && body.message) || ('HTTP ' + r.status));
|
|
122
|
+
return body;
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
function act(method, path, confirmText) {
|
|
127
|
+
if (confirmText && !confirm(confirmText)) return Promise.resolve();
|
|
128
|
+
return api(method, path).then(function () { refresh(); }).catch(function (e) { toast(e.message); });
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
var size = function (b) { if (!b && b !== 0) return ''; var u = ['B', 'KB', 'MB', 'GB', 'TB']; var i = 0; while (b >= 1024 && i < u.length - 1) { b /= 1024; i++; } return (i ? b.toFixed(1) : b) + ' ' + u[i]; };
|
|
132
|
+
var ago = function (s) { var d = Date.now() / 1000 - s; if (d < 60) return 'just now'; if (d < 3600) return Math.floor(d / 60) + ' min ago'; if (d < 86400) return Math.floor(d / 3600) + ' h ago'; return Math.floor(d / 86400) + ' d ago'; };
|
|
133
|
+
var short = function (id) { return String(id || '').replace(/^sha256:/, '').slice(0, 12); };
|
|
134
|
+
|
|
135
|
+
var TABS = ['Containers', 'Images', 'Volumes', 'Networks', 'System'];
|
|
136
|
+
var state = { tab: 'Containers', all: true, selected: null };
|
|
137
|
+
var view = document.getElementById('view');
|
|
138
|
+
|
|
139
|
+
function renderTabs() {
|
|
140
|
+
var nav = document.getElementById('tabs');
|
|
141
|
+
nav.textContent = '';
|
|
142
|
+
TABS.forEach(function (name) {
|
|
143
|
+
nav.appendChild(el('button', { text: name, class: state.tab === name ? 'active' : '', onclick: function () { state.tab = name; state.selected = null; stopDetail(); renderTabs(); refresh(); } }));
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function table(headers, rows, empty) {
|
|
148
|
+
if (!rows.length) return el('div', { class: 'empty', text: empty });
|
|
149
|
+
return el('table', null, [el('thead', null, [el('tr', null, headers.map(function (h) { return el('th', { text: h }); }))]), el('tbody', null, rows)]);
|
|
150
|
+
}
|
|
151
|
+
function btn(label, onclick, opts) { opts = opts || {}; return el('button', { class: 'btn' + (opts.danger ? ' danger' : ''), text: label, disabled: opts.disabled, onclick: function (e) { e.stopPropagation(); onclick(); } }); }
|
|
152
|
+
|
|
153
|
+
/* ── containers ── */
|
|
154
|
+
function containers() {
|
|
155
|
+
return api('GET', '/containers/json' + (state.all ? '?all=1' : '')).then(function (list) {
|
|
156
|
+
var bar = el('div', { class: 'toolbar' }, [
|
|
157
|
+
el('label', null, [el('input', { type: 'checkbox', checked: state.all, onchange: function (e) { state.all = e.target.checked; refresh(); } }), 'show stopped']),
|
|
158
|
+
el('span', { class: 'spacer' }),
|
|
159
|
+
btn('Remove stopped', function () { act('POST', '/containers/prune', 'Remove every stopped container?'); }),
|
|
160
|
+
]);
|
|
161
|
+
var rows = list.map(function (c) {
|
|
162
|
+
var name = (c.Names && c.Names[0] || '').replace(/^\//, '');
|
|
163
|
+
var st = c.State;
|
|
164
|
+
var ports = (c.Ports || []).filter(function (p) { return p.PublicPort; }).map(function (p) { return p.PublicPort + '→' + p.PrivatePort + (p.Type === 'udp' ? '/udp' : ''); }).join(', ');
|
|
165
|
+
var id = c.Id;
|
|
166
|
+
var acts = el('div', { class: 'actions' }, [
|
|
167
|
+
st === 'running' ? btn('Stop', function () { act('POST', '/containers/' + id + '/stop'); }) : btn('Start', function () { act('POST', '/containers/' + id + '/start'); }, { disabled: st === 'removing' }),
|
|
168
|
+
btn('Restart', function () { act('POST', '/containers/' + id + '/restart'); }, { disabled: st !== 'running' }),
|
|
169
|
+
st === 'paused' ? btn('Resume', function () { act('POST', '/containers/' + id + '/unpause'); }) : btn('Pause', function () { act('POST', '/containers/' + id + '/pause'); }, { disabled: st !== 'running' }),
|
|
170
|
+
btn('Remove', function () { act('DELETE', '/containers/' + id + '?force=1', 'Remove ' + name + '? Its anonymous volumes stay.'); }, { danger: true }),
|
|
171
|
+
]);
|
|
172
|
+
return el('tr', { class: 'row' + (state.selected === id ? ' sel' : ''), onclick: function () { state.selected = state.selected === id ? null : id; refresh(); } }, [
|
|
173
|
+
el('td', null, [el('div', { text: name }), el('div', { class: 'muted mono', text: short(id) })]),
|
|
174
|
+
el('td', { class: 'mono', text: c.Image }),
|
|
175
|
+
el('td', null, [el('span', { class: 'badge ' + st, text: c.Status || st })]),
|
|
176
|
+
el('td', { class: 'mono', text: ports }),
|
|
177
|
+
el('td', { class: 'muted', text: ago(c.Created) }),
|
|
178
|
+
el('td', null, [acts]),
|
|
179
|
+
]);
|
|
180
|
+
});
|
|
181
|
+
view.textContent = '';
|
|
182
|
+
view.appendChild(bar);
|
|
183
|
+
view.appendChild(table(['Name', 'Image', 'Status', 'Ports', 'Created', ''], rows, 'No containers yet. Start one with: thinknsh run -d nginx:alpine'));
|
|
184
|
+
var sel = list.filter(function (c) { return c.Id === state.selected; })[0];
|
|
185
|
+
if (sel) detail(sel); else stopDetail();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
var live = { id: null, logs: null, stats: null };
|
|
190
|
+
function stopDetail() {
|
|
191
|
+
if (live.logs) live.logs.abort();
|
|
192
|
+
if (live.stats) clearInterval(live.stats);
|
|
193
|
+
live = { id: null, logs: null, stats: null };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function detail(c) {
|
|
197
|
+
var running = c.State === 'running';
|
|
198
|
+
var key = c.Id + ':' + c.State;
|
|
199
|
+
if (live.id === key && document.getElementById('detail')) { view.appendChild(document.getElementById('detail')); return; }
|
|
200
|
+
stopDetail();
|
|
201
|
+
live.id = key;
|
|
202
|
+
var logs = el('pre', { class: 'logs', text: '' });
|
|
203
|
+
var cpu = el('div', { class: 'v', text: '–' }), cpuBar = el('i'), mem = el('div', { class: 'v', text: '–' }), memBar = el('i'), net = el('div', { class: 'v', text: '–' }), pids = el('div', { class: 'v', text: '–' });
|
|
204
|
+
var info = el('dl');
|
|
205
|
+
var box = el('div', { class: 'detail', id: 'detail' }, [
|
|
206
|
+
el('div', null, [el('div', { class: 'toolbar' }, [el('strong', { text: 'Logs' }), el('span', { class: 'muted', text: running ? 'following' : 'last 500 lines' })]), logs]),
|
|
207
|
+
el('div', { class: 'stats' }, [
|
|
208
|
+
el('div', { class: 'stat' }, [el('div', { class: 'k', text: 'CPU' }), cpu, el('div', { class: 'bar' }, [cpuBar])]),
|
|
209
|
+
el('div', { class: 'stat' }, [el('div', { class: 'k', text: 'Memory' }), mem, el('div', { class: 'bar' }, [memBar])]),
|
|
210
|
+
el('div', { class: 'stat' }, [el('div', { class: 'k', text: 'Network in / out' }), net]),
|
|
211
|
+
el('div', { class: 'stat' }, [el('div', { class: 'k', text: 'Processes' }), pids]),
|
|
212
|
+
el('div', { class: 'stat' }, [info]),
|
|
213
|
+
]),
|
|
214
|
+
]);
|
|
215
|
+
view.appendChild(box);
|
|
216
|
+
|
|
217
|
+
api('GET', '/containers/' + c.Id + '/json').then(function (d) {
|
|
218
|
+
var add = function (k, v) { if (v) { info.appendChild(el('dt', { text: k })); info.appendChild(el('dd', { class: 'mono', text: v })); } };
|
|
219
|
+
add('ID', short(d.Id));
|
|
220
|
+
add('Command', [d.Path].concat(d.Args || []).join(' '));
|
|
221
|
+
add('IP', d.NetworkSettings && d.NetworkSettings.IPAddress);
|
|
222
|
+
add('Networks', Object.keys((d.NetworkSettings && d.NetworkSettings.Networks) || {}).join(', '));
|
|
223
|
+
add('Mounts', (d.Mounts || []).map(function (m) { return (m.Name || m.Source) + ' → ' + m.Destination; }).join('\n'));
|
|
224
|
+
add('Restart', d.HostConfig && d.HostConfig.RestartPolicy && d.HostConfig.RestartPolicy.Name);
|
|
225
|
+
streamLogs(c.Id, !!(d.Config && d.Config.Tty), running, logs);
|
|
226
|
+
}).catch(function (e) { toast(e.message); });
|
|
227
|
+
|
|
228
|
+
if (running) {
|
|
229
|
+
var tick = function () {
|
|
230
|
+
api('GET', '/containers/' + c.Id + '/stats?stream=false').then(function (s) {
|
|
231
|
+
if (!s || !s.cpu_stats) return;
|
|
232
|
+
var cd = s.cpu_stats.cpu_usage.total_usage - ((s.precpu_stats.cpu_usage || {}).total_usage || 0);
|
|
233
|
+
var sd = s.cpu_stats.system_cpu_usage - (s.precpu_stats.system_cpu_usage || 0);
|
|
234
|
+
var n = s.cpu_stats.online_cpus || 1;
|
|
235
|
+
var pct = sd > 0 && cd >= 0 ? cd / sd * n * 100 : 0;
|
|
236
|
+
cpu.textContent = pct.toFixed(1) + ' %'; cpuBar.style.width = Math.min(100, pct / n) + '%';
|
|
237
|
+
var used = s.memory_stats.usage || 0, lim = s.memory_stats.limit || 0;
|
|
238
|
+
mem.textContent = size(used) + (lim ? ' / ' + size(lim) : ''); memBar.style.width = lim ? Math.min(100, used / lim * 100) + '%' : '0';
|
|
239
|
+
var rx = 0, tx = 0; for (var k in (s.networks || {})) { rx += s.networks[k].rx_bytes; tx += s.networks[k].tx_bytes; }
|
|
240
|
+
net.textContent = size(rx) + ' / ' + size(tx);
|
|
241
|
+
pids.textContent = String((s.pids_stats && s.pids_stats.current) || s.num_procs || '–');
|
|
242
|
+
}).catch(function () {});
|
|
243
|
+
};
|
|
244
|
+
tick();
|
|
245
|
+
live.stats = setInterval(tick, 2000);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Logs: 8-byte frame headers unless the container has a TTY.
|
|
250
|
+
function streamLogs(id, tty, follow, pre) {
|
|
251
|
+
var ctl = new AbortController();
|
|
252
|
+
live.logs = ctl;
|
|
253
|
+
fetch('/api/containers/' + id + '/logs?stdout=1&stderr=1&tail=500' + (follow ? '&follow=1' : ''), { headers: { 'x-tnc-ui': token }, signal: ctl.signal }).then(function (r) {
|
|
254
|
+
if (!r.ok || !r.body) return;
|
|
255
|
+
var reader = r.body.getReader(), dec = new TextDecoder(), buf = new Uint8Array(0);
|
|
256
|
+
var push = function (bytes) {
|
|
257
|
+
var atEnd = pre.scrollTop + pre.clientHeight >= pre.scrollHeight - 20;
|
|
258
|
+
pre.appendChild(document.createTextNode(dec.decode(bytes, { stream: true })));
|
|
259
|
+
if (pre.childNodes.length > 4000) pre.removeChild(pre.firstChild);
|
|
260
|
+
if (atEnd) pre.scrollTop = pre.scrollHeight;
|
|
261
|
+
};
|
|
262
|
+
var pump = function () {
|
|
263
|
+
return reader.read().then(function (res) {
|
|
264
|
+
if (res.done) return;
|
|
265
|
+
if (tty) { push(res.value); return pump(); }
|
|
266
|
+
var next = new Uint8Array(buf.length + res.value.length); next.set(buf); next.set(res.value, buf.length); buf = next;
|
|
267
|
+
while (buf.length >= 8) {
|
|
268
|
+
var len = (buf[4] << 24 >>> 0) + (buf[5] << 16) + (buf[6] << 8) + buf[7];
|
|
269
|
+
if (buf.length < 8 + len) break;
|
|
270
|
+
push(buf.subarray(8, 8 + len));
|
|
271
|
+
buf = buf.slice(8 + len);
|
|
272
|
+
}
|
|
273
|
+
return pump();
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
return pump();
|
|
277
|
+
}).catch(function () {});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/* ── images, volumes, networks, system ── */
|
|
281
|
+
function images() {
|
|
282
|
+
return api('GET', '/images/json').then(function (list) {
|
|
283
|
+
var rows = list.map(function (i) {
|
|
284
|
+
var tags = (i.RepoTags || []).filter(function (t) { return t !== '<none>:<none>'; });
|
|
285
|
+
var ref = tags[0] || i.Id;
|
|
286
|
+
return el('tr', null, [
|
|
287
|
+
el('td', { class: 'mono', text: tags.join(', ') || '<untagged>' }),
|
|
288
|
+
el('td', { class: 'mono muted', text: short(i.Id) }),
|
|
289
|
+
el('td', { text: size(i.Size) }),
|
|
290
|
+
el('td', { text: String(i.Containers > 0 ? i.Containers : '') }),
|
|
291
|
+
el('td', { class: 'muted', text: ago(i.Created) }),
|
|
292
|
+
el('td', null, [btn('Remove', function () { act('DELETE', '/images/' + encodeURIComponent(ref), 'Remove image ' + ref + '?'); }, { danger: true })]),
|
|
293
|
+
]);
|
|
294
|
+
});
|
|
295
|
+
view.textContent = '';
|
|
296
|
+
view.appendChild(el('div', { class: 'toolbar' }, [el('span', { class: 'spacer' }), btn('Remove untagged', function () { act('POST', '/images/prune', 'Remove every untagged image no container uses?'); })]));
|
|
297
|
+
view.appendChild(table(['Image', 'ID', 'Size', 'In use', 'Created', ''], rows, 'No images yet.'));
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function volumes() {
|
|
302
|
+
return api('GET', '/volumes').then(function (d) {
|
|
303
|
+
var rows = (d.Volumes || []).map(function (v) {
|
|
304
|
+
var labels = Object.keys(v.Labels || {}).map(function (k) { return k + '=' + v.Labels[k]; }).join(', ');
|
|
305
|
+
return el('tr', null, [
|
|
306
|
+
el('td', { class: 'mono', text: v.Name }),
|
|
307
|
+
el('td', { class: 'muted', text: labels }),
|
|
308
|
+
el('td', { class: 'mono muted', text: v.Mountpoint }),
|
|
309
|
+
el('td', null, [btn('Remove', function () { act('DELETE', '/volumes/' + encodeURIComponent(v.Name), 'Remove volume ' + v.Name + ' and all its data?'); }, { danger: true })]),
|
|
310
|
+
]);
|
|
311
|
+
});
|
|
312
|
+
view.textContent = '';
|
|
313
|
+
view.appendChild(el('div', { class: 'toolbar' }, [el('span', { class: 'spacer' }), btn('Remove unused', function () { act('POST', '/volumes/prune', 'Remove every volume no container uses, with its data?'); })]));
|
|
314
|
+
view.appendChild(table(['Volume', 'Labels', 'Where', ''], rows, 'No volumes.'));
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function networks() {
|
|
319
|
+
return api('GET', '/networks').then(function (list) {
|
|
320
|
+
var rows = list.map(function (n) {
|
|
321
|
+
var cfg = (n.IPAM && n.IPAM.Config && n.IPAM.Config[0]) || {};
|
|
322
|
+
var builtin = ['bridge', 'host', 'none'].indexOf(n.Name) !== -1;
|
|
323
|
+
return el('tr', null, [
|
|
324
|
+
el('td', { class: 'mono', text: n.Name }),
|
|
325
|
+
el('td', { text: n.Driver }),
|
|
326
|
+
el('td', { class: 'mono muted', text: cfg.Subnet || '' }),
|
|
327
|
+
el('td', { text: String(Object.keys(n.Containers || {}).length || '') }),
|
|
328
|
+
el('td', null, [builtin ? null : btn('Remove', function () { act('DELETE', '/networks/' + encodeURIComponent(n.Id), 'Remove network ' + n.Name + '?'); }, { danger: true })]),
|
|
329
|
+
]);
|
|
330
|
+
});
|
|
331
|
+
view.textContent = '';
|
|
332
|
+
view.appendChild(table(['Network', 'Driver', 'Subnet', 'Containers', ''], rows, 'No networks.'));
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function system() {
|
|
337
|
+
return Promise.all([api('GET', '/version'), api('GET', '/info'), api('GET', '/system/df')]).then(function (r) {
|
|
338
|
+
var v = r[0] || {}, i = r[1] || {}, df = r[2] || {};
|
|
339
|
+
var vols = (df.Volumes || []).reduce(function (a, x) { return a + ((x.UsageData && x.UsageData.Size > 0) ? x.UsageData.Size : 0); }, 0);
|
|
340
|
+
var cache = (df.BuildCache || []).reduce(function (a, x) { return a + (x.Size || 0); }, 0);
|
|
341
|
+
var tile = function (k, val) { return el('div', { class: 'stat' }, [el('div', { class: 'k', text: k }), el('div', { class: 'v', text: val })]); };
|
|
342
|
+
view.textContent = '';
|
|
343
|
+
view.appendChild(el('div', { class: 'grid' }, [
|
|
344
|
+
tile('Engine', 'dock ' + (v.Version || '')),
|
|
345
|
+
tile('API', v.ApiVersion || ''),
|
|
346
|
+
tile('Linux kernel', i.KernelVersion || v.KernelVersion || ''),
|
|
347
|
+
tile('CPUs / memory', (i.NCPU || '–') + ' / ' + size(i.MemTotal)),
|
|
348
|
+
tile('Containers', (i.ContainersRunning || 0) + ' running of ' + (i.Containers || 0)),
|
|
349
|
+
tile('Images', (i.Images || 0) + ' · ' + size(df.LayersSize)),
|
|
350
|
+
tile('Volumes', String((df.Volumes || []).length) + (vols ? ' · ' + size(vols) : '')),
|
|
351
|
+
tile('Build cache', size(cache)),
|
|
352
|
+
]));
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
var VIEWS = { Containers: containers, Images: images, Volumes: volumes, Networks: networks, System: system };
|
|
357
|
+
var pending = null;
|
|
358
|
+
function refresh() {
|
|
359
|
+
clearTimeout(pending);
|
|
360
|
+
pending = setTimeout(function () { VIEWS[state.tab]().catch(function (e) { toast(e.message); }); }, 60);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Live updates: re-render on engine events (reconnects if the stream ends).
|
|
364
|
+
function events() {
|
|
365
|
+
fetch('/api/events', { headers: { 'x-tnc-ui': token } }).then(function (r) {
|
|
366
|
+
if (!r.ok || !r.body) throw new Error('events');
|
|
367
|
+
var reader = r.body.getReader();
|
|
368
|
+
var pump = function () { return reader.read().then(function (res) { if (res.done) throw new Error('ended'); refresh(); return pump(); }); };
|
|
369
|
+
return pump();
|
|
370
|
+
}).catch(function () { setTimeout(events, 3000); });
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
if (!token) { document.getElementById('locked').hidden = false; document.getElementById('tabs').hidden = true; view.parentNode.hidden = true; return; }
|
|
374
|
+
api('GET', '/version').then(function (v) {
|
|
375
|
+
document.getElementById('dot').className = 'dot on';
|
|
376
|
+
document.getElementById('meta').textContent = 'dock ' + v.Version + ' · API ' + v.ApiVersion + ' · ' + (v.Os || '') + '/' + (v.Arch || '');
|
|
377
|
+
}).catch(function (e) { document.getElementById('meta').textContent = e.message; });
|
|
378
|
+
renderTabs();
|
|
379
|
+
refresh();
|
|
380
|
+
events();
|
|
381
|
+
})();
|
|
382
|
+
</script>
|
|
383
|
+
</body>
|
|
384
|
+
</html>
|
|
385
|
+
`;module.exports={PAGE};
|
|
@@ -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 http=require("http"),crypto=require("crypto"),{PAGE}=require("./page"),ID="[^/]+",ALLOWED=[["GET",/^\/.*$/],["POST",new RegExp(`^/containers/${ID}/(start|stop|restart|kill|pause|unpause)$`)],["POST",/^\/(containers|images|volumes|networks)\/prune$/],["DELETE",new RegExp(`^/(containers|volumes|networks)/${ID}$`)],["DELETE",/^\/images\/.+$/]],BLOCKED=/\/(archive|export|attach|exec)$|^\/exec\/|^\/images\/(.+\/)?get$|^\/(session|grpc)$/;function allowed(e,t){return!BLOCKED.test(t)&&ALLOWED.some(([n,o])=>n===e&&o.test(t))}function sameToken(e,t){const n=Buffer.from(String(e||"")),o=Buffer.from(String(t||""));return n.length===o.length&&crypto.timingSafeEqual(n,o)}function startUi({socketPath:e,port:t=0,token:n=crypto.randomBytes(24).toString("hex")}={}){let o=null;const r=http.createServer((t,r)=>{const s=new URL(t.url,"http://ui"),a=(e,t)=>{r.writeHead(e,{"Content-Type":"application/json","Cache-Control":"no-store"}),r.end(JSON.stringify({message:t}))};if(i=t.headers.host,null===o||i!==`127.0.0.1:${o}`&&i!==`localhost:${o}`)return a(421,"wrong Host");var i;const c={"Cache-Control":"no-store","X-Content-Type-Options":"nosniff","X-Frame-Options":"DENY","Referrer-Policy":"no-referrer"};if("GET"===t.method&&("/"===s.pathname||"/index.html"===s.pathname))return r.writeHead(200,{...c,"Content-Type":"text/html; charset=utf-8","Content-Security-Policy":"default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'; img-src data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'"}),r.end(PAGE);if(!s.pathname.startsWith("/api/"))return a(404,"not found");if(!sameToken(t.headers["x-tnc-ui"],n))return a(403,"missing or wrong session token");const l=s.pathname.slice(4);if(!allowed(t.method,l))return a(403,`${t.method} ${l} is not available from the dashboard`);const p=http.request({socketPath:e,method:t.method,path:l+s.search,headers:{"Content-Type":t.headers["content-type"]||"application/json",...t.headers["content-length"]?{"Content-Length":t.headers["content-length"]}:{}}},e=>{r.writeHead(e.statusCode,{...c,"Content-Type":e.headers["content-type"]||"application/octet-stream"}),e.pipe(r),r.on("close",()=>e.destroy())});p.on("error",e=>{r.headersSent?r.end():a(502,`dock is not reachable: ${e.message}`)}),t.pipe(p)});return new Promise((e,s)=>{r.once("error",s),r.listen(t,"127.0.0.1",()=>{o=r.address().port,e({port:o,token:n,url:`http://127.0.0.1:${o}/#t=${n}`,close:()=>new Promise(e=>{r.closeAllConnections?.(),r.close(()=>e())})})})})}module.exports={startUi,allowed};
|
|
@@ -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";module.exports={DOCK_VERSION:"0.1.0",API_VERSION:"1.44",MIN_API_VERSION:"1.24",PHASE:"P7"};
|
|
@@ -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"),{spawn,spawnSync}=require("child_process"),APP_DIRS=()=>["/Applications",path.join(os.homedir(),"Applications")],APPS={cursor:{label:"Cursor",kind:"editor",apps:["Cursor.app"],cli:"Contents/Resources/app/bin/cursor",extDir:".cursor/extensions",builtinSsh:!0},antigravity:{label:"Antigravity IDE",kind:"editor",apps:["Antigravity IDE.app"],cli:"Contents/Resources/app/bin/antigravity-ide",extDir:".antigravity-ide/extensions",builtinSsh:!0},vscode:{label:"VS Code",kind:"editor",apps:["Visual Studio Code.app"],cli:"Contents/Resources/app/bin/code",extDir:".vscode/extensions",builtinSsh:!1},claude:{label:"Claude desktop",kind:"agent",apps:["Claude.app"]},codex:{label:"Codex app",kind:"agent",apps:["Codex.app"]}};function findBundle(e,n=APP_DIRS()){for(const t of n)for(const n of e){const e=path.join(t,n);if(fs.existsSync(e))return e}return null}function cliIn(e,n){if(!e||!n)return null;const t=path.join(e,n);if(fs.existsSync(t))return t;const i=path.dirname(t);try{const e=fs.readdirSync(i).filter(e=>!e.includes(".")&&fs.statSync(path.join(i,e)).isFile());if(1===e.length)return path.join(i,e[0])}catch(e){}return null}function hasSshExtension(e){try{return fs.readdirSync(path.join(os.homedir(),e)).some(e=>/remote-ssh|open-remote-ssh/i.test(e))}catch(e){return!1}}function detectApps({dirs:e}={}){const n=[];for(const[t,i]of Object.entries(APPS)){const o=findBundle(i.apps,e),s={id:t,label:i.label,kind:i.kind,bundle:o,cli:null,ready:!1,note:""};if(o)if("editor"===i.kind){s.cli=cliIn(o,i.cli);const e=i.builtinSsh||hasSshExtension(i.extDir);s.ready=!!s.cli&&e,s.note=s.cli?e?"opens straight onto the workspace":'install the "Remote - SSH" extension first':"command-line launcher not found in the app"}else s.ready=!0,s.note="claude"===t?"Code tab → environment picker → SSH → pick the host":"Settings → Connections → enable the host → pick a folder";else s.note="antigravity"===t&&findBundle(["Antigravity.app"],e)?"only the Antigravity agent manager is installed; it has no SSH. Install Antigravity IDE into /Applications":"not installed";n.push(s)}return n}function remoteUri(e,n){return`vscode-remote://ssh-remote+${e}${n.startsWith("/")?n:`/${n}`}`}function openEditor(e,n,t){const i=[t.endsWith(".code-workspace")?"--file-uri":"--folder-uri",remoteUri(n,t),"--new-window"];return spawn(e.cli,i,{detached:!0,stdio:"ignore"}).unref(),[e.cli,...i]}function launchApp(e){"darwin"===process.platform&&spawnSync("open",[e])}module.exports={APPS,detectApps,remoteUri,openEditor,launchApp,findBundle,cliIn};
|
|
@@ -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"),net=require("net"),path=require("path"),crypto=require("crypto"),{spawnSync}=require("child_process"),{BUILD_FILE}=require("../api/wire"),USER="tnc",SSH_PORT_IN=2222,FIRST_HOST_PORT=22220,LABEL="tnc.workspace",DEFAULT_AGENTS=["@anthropic-ai/claude-code","@openai/codex"],AGENT_ALIASES={claude:"@anthropic-ai/claude-code",codex:"@openai/codex",gemini:"@google/gemini-cli"},IMAGE_RECIPE=`FROM node:22-bookworm-slim\nARG AGENTS=""\nRUN apt-get update \\\n && apt-get install -y --no-install-recommends openssh-server git ca-certificates curl wget less procps ripgrep python3 python3-venv openssh-client \\\n && rm -rf /var/lib/apt/lists/* \\\n && groupmod -n ${USER} node && usermod -l ${USER} -d /home/${USER} -m -s /bin/bash node \\\n && mkdir -p /run/sshd /etc/tnc-workspace\nRUN find / -xdev -perm /6000 -type f -exec chmod a-s {} + ; true\nRUN if [ -n "$AGENTS" ]; then npm install -g $AGENTS && npm cache clean --force; fi\nCOPY profile.sh /etc/profile.d/tnc-workspace.sh\nCOPY sshd_config /etc/tnc-workspace/sshd_config\nCOPY entrypoint.sh /usr/local/bin/tnc-workspace-entry\nRUN chmod 755 /usr/local/bin/tnc-workspace-entry && chmod 644 /etc/tnc-workspace/sshd_config\nENV TNC_WORKSPACE=1 NPM_CONFIG_PREFIX=/home/${USER}/.local PATH=/home/${USER}/.local/bin:$PATH\nWORKDIR /work\nEXPOSE 2222\nCMD ["/usr/local/bin/tnc-workspace-entry"]\n`,PROFILE='export NPM_CONFIG_PREFIX="$HOME/.local"\nexport PATH="$HOME/.local/bin:$PATH"\nexport TNC_WORKSPACE=1\ncd /work 2>/dev/null || true\n',SSHD_CONFIG=`Port 2222\nHostKey /etc/tnc-workspace/ssh_host_ed25519_key\nPidFile /tmp/tnc-sshd.pid\nAuthorizedKeysFile /etc/tnc-workspace/authorized_keys\nAllowUsers ${USER}\nPubkeyAuthentication yes\nPasswordAuthentication no\nKbdInteractiveAuthentication no\nPermitRootLogin no\nPermitEmptyPasswords no\nUsePAM no\nStrictModes yes\nPermitUserEnvironment no\nAllowAgentForwarding no\nAllowTcpForwarding local\nGatewayPorts no\nPermitTunnel no\nX11Forwarding no\nAcceptEnv LANG LC_*\nSetEnv TNC_WORKSPACE=1 NPM_CONFIG_PREFIX=/home/${USER}/.local PATH=/home/${USER}/.local/bin:/usr/local/bin:/usr/bin:/bin\nSubsystem sftp internal-sftp\nPrintMotd no\n# loopback only: editors and probes open many short connections; don't drop new ones early\nMaxStartups 100:30:200\nLoginGraceTime 30\n`,DROP=`setpriv --reuid=${USER} --regid=${USER} --init-groups --inh-caps=-all --bounding-set=-all env HOME=/home/${USER} USER=${USER} LOGNAME=${USER}`,ENTRYPOINT=`#!/bin/sh\nset -eu\nsrc=/run/tnc-workspace\n[ -f "$src/ssh_host_ed25519_key" ] && [ -f "$src/authorized_keys" ] || { echo "tnc workspace: keys missing in $src" >&2; exit 1; }\ninstall -o ${USER} -g ${USER} -m 600 "$src/ssh_host_ed25519_key" /etc/tnc-workspace/ssh_host_ed25519_key\ninstall -o root -g root -m 644 "$src/authorized_keys" /etc/tnc-workspace/authorized_keys\n[ "$(stat -c %U /home/${USER})" = "${USER}" ] || chown ${USER}:${USER} /home/${USER}\nchmod 700 /home/${USER}\nexec ${DROP} /usr/sbin/sshd -D -e -f /etc/tnc-workspace/sshd_config\n`;function workspaceError(e){const n=new Error(e);return n.workspace=!0,n}function workspaceName(e){const n=String(e||"").toLowerCase().replace(/[^a-z0-9_.-]+/g,"-").replace(/^[-_.]+|[-_.]+$/g,"").slice(0,40);if(!n)throw workspaceError(`cannot make a workspace name from "${e}"; pass --name`);return n}const containerName=e=>`tnc-ws-${e}`,homeVolume=e=>`tnc-ws-${e}-home`,sshHost=e=>`tnc-${e}`;function agentPackages(e){return null==e||""===e?DEFAULT_AGENTS:"none"===e?[]:[...new Set(String(e).split(/[\s,]+/).filter(Boolean).map(e=>AGENT_ALIASES[e]||e))].map(e=>{if(!/^(@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*(@[\w.^~<>=-]+)?$/i.test(e))throw workspaceError(`invalid agent package "${e}"`);return e})}function imageTag(e){return`tnc-workspace:${crypto.createHash("sha256").update(IMAGE_RECIPE).update(SSHD_CONFIG).update(ENTRYPOINT).update(PROFILE).update(e.join(" ")).digest("hex").slice(0,12)}`}function writeImageContext(e){fs.mkdirSync(e,{recursive:!0}),fs.writeFileSync(path.join(e,BUILD_FILE),IMAGE_RECIPE),fs.writeFileSync(path.join(e,"sshd_config"),SSHD_CONFIG),fs.writeFileSync(path.join(e,"profile.sh"),PROFILE),fs.writeFileSync(path.join(e,"entrypoint.sh"),ENTRYPOINT,{mode:493})}function workspacesDir(e){return path.join(e,"workspaces")}function stateDir(e,n){return path.join(workspacesDir(e),n)}function readMeta(e,n){try{return JSON.parse(fs.readFileSync(path.join(stateDir(e,n),"workspace.json"),"utf8"))}catch(e){return null}}function listMeta(e){let n=[];try{n=fs.readdirSync(workspacesDir(e))}catch(e){}return n.map(n=>readMeta(e,n)).filter(Boolean).sort((e,n)=>e.name.localeCompare(n.name))}function keygen(e,n){const t=spawnSync("ssh-keygen",["-q","-t","ed25519","-N","","-C",n,"-f",e],{encoding:"utf8"});if(t.error)throw workspaceError("ssh-keygen was not found; install OpenSSH (it ships with macOS, Linux and Windows 10+)");if(0!==t.status)throw workspaceError(`ssh-keygen failed: ${(t.stderr||"").trim()}`)}function ensureKeys(e,n,t){const o=stateDir(e,n),r=path.join(o,"client"),s=path.join(o,"server");for(const n of[workspacesDir(e),o,r,s])fs.mkdirSync(n,{recursive:!0,mode:448});for(const e of[o,r,s])fs.chmodSync(e,448);const i=path.join(r,"id_ed25519"),a=path.join(s,"ssh_host_ed25519_key");fs.existsSync(i)||keygen(i,`thinknsh-workspace-${n}`),fs.existsSync(a)||keygen(a,`tnc-workspace-host-${n}`);const c=fs.readFileSync(`${i}.pub`,"utf8").trim();fs.writeFileSync(path.join(s,"authorized_keys"),`no-agent-forwarding,no-X11-forwarding ${c}\n`,{mode:384}),fs.rmSync(`${a}.pub`,{force:!0});const p=spawnSync("ssh-keygen",["-y","-f",a],{encoding:"utf8"}).stdout.trim(),h=path.join(o,"known_hosts");return fs.writeFileSync(h,`[127.0.0.1]:${t} ${p}\n`,{mode:384}),{dir:o,id:i,server:s,knownHosts:h}}function saveMeta(e,n){const t=path.join(stateDir(e,n.name),"workspace.json");fs.writeFileSync(t,JSON.stringify(n,null,2)+"\n",{mode:384})}function removeState(e,n){fs.rmSync(stateDir(e,n),{recursive:!0,force:!0})}const quote=e=>/[\s"]/.test(e)?`"${e.replace(/"/g,'\\"')}"`:e;function hostEntry(e,n){const t=stateDir(n,e.name);return[`Host ${sshHost(e.name)}`," HostName 127.0.0.1",` Port ${e.port}`,` User ${USER}`,` IdentityFile ${quote(path.join(t,"client","id_ed25519"))}`," IdentitiesOnly yes",` UserKnownHostsFile ${quote(path.join(t,"known_hosts"))}`," StrictHostKeyChecking yes"," ForwardAgent no"," ForwardX11 no",""].join("\n")}function writeSshConfig(e){const n=path.join(workspacesDir(e),"ssh_config");fs.mkdirSync(path.dirname(n),{recursive:!0,mode:448});const t=["# thinknsh workspaces; generated, edits are overwritten","",...listMeta(e).map(n=>hostEntry(n,e))].join("\n");return fs.writeFileSync(n,t,{mode:384}),n}function userSshConfig(){return process.env.TNC_SSH_CONFIG||path.join(os.homedir(),".ssh","config")}const INCLUDE_MARK="# thinknsh workspaces (thinknsh workspace up)",BLOCK_START="# >>> thinknsh workspaces (managed by thinknsh workspace; edits here are overwritten) >>>",BLOCK_END="# <<< thinknsh workspaces <<<";function syncUserSshConfig(e,n=userSshConfig()){let t="";try{t=fs.readFileSync(n,"utf8")}catch(e){if("ENOENT"!==e.code)throw e}const o=`Include ${quote(path.join(workspacesDir(e),"ssh_config"))}`,r=t.split(/\r?\n/),s=[];let i=!1;for(const e of r)e.trim()!==BLOCK_START?e.trim()!==BLOCK_END?i||e.trim()===INCLUDE_MARK||e.trim()===o||s.push(e):i=!1:i=!0;for(;s.length&&""===s[s.length-1].trim();)s.pop();for(;s.length&&""===s[0].trim();)s.shift();const a=listMeta(e).map(n=>hostEntry(n,e).trimEnd()),c=a.length?[BLOCK_START,...a.flatMap(e=>[e,""]),BLOCK_END]:[],p=[...s,...s.length&&c.length?[""]:[],...c].join("\n")+(s.length||c.length?"\n":"");if(p===t)return!1;fs.mkdirSync(path.dirname(n),{recursive:!0,mode:448}),fs.writeFileSync(n,p,{mode:384});try{fs.chmodSync(n,384)}catch(e){}return!0}function folderMounts(e){if(!e.length)throw workspaceError("no project folder given");if(1===e.length)return[{host:e[0],target:"/work"}];const n=new Map;return e.map(e=>{const t=path.basename(e);if(!/^[\w.@+-]+$/.test(t))throw workspaceError(`folder name "${t}" has characters that cannot be a path in the workspace; rename or pick another folder`);if(n.has(t))throw workspaceError(`two folders are both named "${t}" (${n.get(t)} and ${e}); use separate workspaces`);return n.set(t,e),{host:e,target:`/work/${t}`}})}function writeCodeWorkspace(e,n,t){const o=path.join(e,`${n}.code-workspace`),r={folders:t.map(e=>({name:path.basename(e.host),path:e.target})),settings:{}};return fs.writeFileSync(o,JSON.stringify(r,null,2)+"\n",{mode:420}),`/run/tnc-workspace/${n}.code-workspace`}function networkChoice(e,{network:n=[],compose:t,noCompose:o=!1}={}){let r=e?.networks||[];if(n.length){const e=n.flatMap(e=>String(e).split(",")).map(e=>e.trim()).filter(Boolean);r=e.includes("none")?[]:[...new Set(e)];for(const e of r){if(!/^[a-zA-Z0-9][a-zA-Z0-9_.-]*$/.test(e))throw workspaceError(`invalid network name "${e}"`);if(["host","none","null"].includes(e))throw workspaceError(`a workspace cannot join the "${e}" network`)}}let s=e&&"compose"in e?e.compose:"auto";if(o&&void 0!==t)throw workspaceError("--compose and --no-compose together");return o?s=!1:void 0!==t&&(s=""===t?"auto":t),{networks:r,compose:s}}function wantedNetworks(e,n=[]){return[...new Set([...e,...n.map(e=>e.name)])]}function portFree(e){return new Promise(n=>{const t=net.createServer();t.once("error",()=>n(!1)),t.listen(e,"127.0.0.1",()=>t.close(()=>n(!0)))})}async function pickPort(e=[]){for(let n=22220;n<22720;n++)if(!e.includes(n)&&await portFree(n))return n;throw workspaceError("no free port in 22220-22719; pass --port")}function containerBody({name:e,image:n,mounts:t,serverDir:o,port:r,memory:s,nanoCpus:i,pidsLimit:a=4096}){return{Image:n,Hostname:`tnc-${e}`.slice(0,63),Labels:{[LABEL]:e,[`${LABEL}.path`]:t.map(e=>e.host).join("\n"),[`${LABEL}.port`]:String(r)},ExposedPorts:{"2222/tcp":{}},HostConfig:{Binds:[...t.map(e=>`${e.host}:${e.target}`),`${o}:/run/tnc-workspace:ro`,`${homeVolume(e)}:/home/${USER}`],PortBindings:{"2222/tcp":[{HostIp:"127.0.0.1",HostPort:String(r)}]},Memory:s,NanoCpus:i,PidsLimit:a,CapDrop:["ALL"],CapAdd:["CHOWN","DAC_OVERRIDE","FOWNER","SETUID","SETGID","SETPCAP"],SecurityOpt:["no-new-privileges"],RestartPolicy:{Name:"unless-stopped"}}}}module.exports={USER,SSH_PORT_IN:2222,FIRST_HOST_PORT:22220,LABEL,DEFAULT_AGENTS,IMAGE_RECIPE,SSHD_CONFIG,ENTRYPOINT,PROFILE,DROP,workspaceName,containerName,homeVolume,sshHost,agentPackages,imageTag,writeImageContext,stateDir,readMeta,listMeta,saveMeta,removeState,ensureKeys,hostEntry,writeSshConfig,syncUserSshConfig,userSshConfig,INCLUDE_MARK,BLOCK_START,BLOCK_END,folderMounts,writeCodeWorkspace,networkChoice,wantedNetworks,pickPort,portFree,containerBody,workspaceError};
|
|
@@ -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 EventEmitter=require("events"),chalk=require("chalk");class NotificationManager extends EventEmitter{constructor(t={}){super(),this.options={sound:t.sound||!1,desktop:t.desktop||!1,...t},this.notifications=[],this.maxNotifications=100,this.unreadCount=0}show(t){const e={id:`notif_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,type:t.type||"info",title:t.title||"Notification",message:t.message,timestamp:new Date,read:!1,data:t.data||{}};return this.notifications.unshift(e),this.unreadCount++,this.notifications.length>this.maxNotifications&&this.notifications.pop(),this.emit("notification",e),this.showInConsole(e),this.options.desktop&&this.showDesktopNotification(e),this.options.sound&&this.playSound(),e}showInConsole(t){let e=chalk.blue,i="ℹ";switch(t.type){case"success":e=chalk.green,i="[OK]";break;case"warning":e=chalk.yellow,i="[WARN]";break;case"error":e=chalk.red,i="[FAIL]";break;default:e=chalk.blue,i="ℹ"}console.log(e(`${i} ${t.title}: ${t.message}`))}showDesktopNotification(t){if("renderer"===process.type||"browser"===process.type)try{const{Notification:e}=require("electron");new e({title:t.title,body:t.message,silent:!this.options.sound}).show()}catch(t){}}playSound(){process.stdout.write("")}markAsRead(t){const e=this.notifications.find(e=>e.id===t);e&&!e.read&&(e.read=!0,this.unreadCount--,this.emit("read",e))}markAllAsRead(){this.notifications.forEach(t=>{t.read||(t.read=!0)}),this.unreadCount=0,this.emit("allRead")}getUnread(){return this.notifications.filter(t=>!t.read)}getAll(){return this.notifications}clear(){this.notifications=[],this.unreadCount=0,this.emit("cleared")}success(t,e,i={}){return this.show({type:"success",title:t,message:e,data:i})}error(t,e,i={}){return this.show({type:"error",title:t,message:e,data:i})}warning(t,e,i={}){return this.show({type:"warning",title:t,message:e,data:i})}info(t,e,i={}){return this.show({type:"info",title:t,message:e,data:i})}}module.exports=NotificationManager;
|
|
@@ -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");class Logger{constructor(t={}){this.options={logDir:t.logDir||path.join(os.homedir(),".thinkncollab","logs"),level:t.level||"info",maxFiles:t.maxFiles||5,maxSize:t.maxSize||10485760,...t},fs.ensureDirSync(this.options.logDir),this.levels={debug:0,info:1,warn:2,error:3},this.currentLogFile=this.getLogFileName()}getLogFileName(){const t=(new Date).toISOString().split("T")[0];return path.join(this.options.logDir,`thinknsh-${t}.log`)}async write(t,e,i=null){if(this.levels[t]<this.levels[this.options.level])return;const r={timestamp:(new Date).toISOString(),level:t,message:e,data:i,pid:process.pid},s=JSON.stringify(r)+"\n";try{await fs.appendFile(this.currentLogFile,s),await this.rotateIfNeeded()}catch(t){console.error("Failed to write log:",t)}}async rotateIfNeeded(){try{if((await fs.stat(this.currentLogFile)).size>this.options.maxSize){const t=(new Date).toISOString().replace(/[:.]/g,"-"),e=this.currentLogFile.replace(".log",`-${t}.log`);await fs.rename(this.currentLogFile,e),this.currentLogFile=this.getLogFileName(),await this.cleanOldLogs()}}catch(t){}}async cleanOldLogs(){try{const t=(await fs.readdir(this.options.logDir)).filter(t=>t.startsWith("thinknsh-")&&t.endsWith(".log")).map(t=>({name:t,path:path.join(this.options.logDir,t),time:fs.statSync(path.join(this.options.logDir,t)).mtime.getTime()})).sort((t,e)=>e.time-t.time);if(t.length>this.options.maxFiles){const e=t.slice(this.options.maxFiles);for(const t of e)await fs.remove(t.path)}}catch(t){console.error("Failed to clean old logs:",t)}}debug(t,e=null){return this.write("debug",t,e)}info(t,e=null){return this.write("info",t,e)}warn(t,e=null){return this.write("warn",t,e)}error(t,e=null){return this.write("error",t,e)}async getRecentLogs(t=100){try{if(!await fs.pathExists(this.currentLogFile))return[];return(await fs.readFile(this.currentLogFile,"utf8")).split("\n").filter(t=>t.trim()).map(t=>JSON.parse(t)).slice(-t)}catch(t){return console.error("Failed to read logs:",t),[]}}}module.exports=Logger;
|
|
@@ -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");function deriveKey(e,t="thinkncollab-e2ee-salt"){return new Promise((r,a)=>{crypto.pbkdf2(e,t,1e5,32,"sha256",(e,t)=>{if(e)return a(e);r(t)})})}function encryptPayload(e,t,r={}){const a=r.nonce||crypto.randomBytes(12),o=crypto.createCipheriv("aes-256-gcm",t,a);r.aad&&o.setAAD(Buffer.from(String(r.aad),"utf8"));let n=o.update(e,"utf8","hex");n+=o.final("hex");const c=o.getAuthTag().toString("hex");return{ciphertext:n,iv:a.toString("hex"),authTag:c,seq:r.seq||0}}function decryptPayload(e,t,r,a,o={}){const n=Buffer.from(t,"hex"),c=Buffer.from(r,"hex"),f=crypto.createDecipheriv("aes-256-gcm",a,n);f.setAuthTag(c),o.aad&&f.setAAD(Buffer.from(String(o.aad),"utf8"));let u=f.update(e,"hex","utf8");return u+=f.final("utf8"),u}module.exports={deriveKey,encryptPayload,decryptPayload};
|
package/package.json
CHANGED
|
@@ -1,44 +1,69 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "thinkncollab-shell",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"description": "
|
|
5
|
-
"main": "src/core/shell.js",
|
|
3
|
+
"version": "2.0.0-beta.1",
|
|
4
|
+
"description": "Lightweight MicroVM Sandbox & Zero-Daemon Dev Runner: Fast MicroVM Containers, Native Microservices, Live Database Snapshots, and End-to-End Encrypted State Sync",
|
|
5
|
+
"main": "dist/src/core/shell.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"thinknsh": "
|
|
8
|
-
"tnc": "
|
|
7
|
+
"thinknsh": "dist/bin/thinknsh.js",
|
|
8
|
+
"tnc": "dist/bin/thinknsh.js",
|
|
9
|
+
"tnc-dep": "dist/bin/thinknsh.js"
|
|
9
10
|
},
|
|
10
11
|
"scripts": {
|
|
11
|
-
"
|
|
12
|
+
"build": "node scripts/build-obfuscate.js",
|
|
13
|
+
"start": "node dist/bin/thinknsh.js",
|
|
12
14
|
"dev": "nodemon bin/thinknsh.js",
|
|
13
|
-
"test": "
|
|
14
|
-
"prepublishOnly": ""
|
|
15
|
+
"test": "node --test --test-concurrency=1 tests/*.test.js",
|
|
16
|
+
"prepublishOnly": "npm run build"
|
|
15
17
|
},
|
|
16
18
|
"keywords": [
|
|
17
19
|
"shell",
|
|
18
20
|
"terminal",
|
|
19
|
-
"
|
|
21
|
+
"microvm",
|
|
22
|
+
"sandbox",
|
|
23
|
+
"zero-daemon",
|
|
24
|
+
"dev-runner",
|
|
25
|
+
"crun",
|
|
26
|
+
"container",
|
|
27
|
+
"dependency-manager",
|
|
28
|
+
"engine-compat",
|
|
20
29
|
"collaboration",
|
|
21
|
-
"
|
|
30
|
+
"thinkncollab"
|
|
22
31
|
],
|
|
23
32
|
"author": "Raman Singh <raman@thinkncollab.com>",
|
|
24
|
-
"license": "
|
|
33
|
+
"license": "Apache-2.0",
|
|
34
|
+
"homepage": "https://thinkncollab.com",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/ThinkNCollab/shell.git"
|
|
38
|
+
},
|
|
39
|
+
"bugs": {
|
|
40
|
+
"url": "https://github.com/ThinkNCollab/shell/issues"
|
|
41
|
+
},
|
|
25
42
|
"dependencies": {
|
|
26
43
|
"@inquirer/prompts": "^2.3.1",
|
|
27
44
|
"chalk": "^4.1.2",
|
|
28
45
|
"cli-table3": "^0.6.5",
|
|
29
46
|
"figlet": "^1.6.0",
|
|
30
47
|
"fs-extra": "^11.1.1",
|
|
48
|
+
"js-yaml": "^5.4.3",
|
|
31
49
|
"socket.io-client": "^4.8.3"
|
|
32
50
|
},
|
|
33
51
|
"engines": {
|
|
34
|
-
"node": ">=
|
|
52
|
+
"node": ">=22.5.0"
|
|
35
53
|
},
|
|
36
54
|
"files": [
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
55
|
+
"dist/",
|
|
56
|
+
"README.md",
|
|
57
|
+
"LICENSE"
|
|
40
58
|
],
|
|
41
59
|
"devDependencies": {
|
|
42
|
-
"playwright": "^1.59.1"
|
|
60
|
+
"playwright": "^1.59.1",
|
|
61
|
+
"terser": "^5.51.2"
|
|
62
|
+
},
|
|
63
|
+
"optionalDependencies": {
|
|
64
|
+
"better-sqlite3": "^9.6.0"
|
|
65
|
+
},
|
|
66
|
+
"overrides": {
|
|
67
|
+
"tmp": "^0.2.7"
|
|
43
68
|
}
|
|
44
69
|
}
|