@cordfuse/crosstalk 7.0.0-alpha.3 → 7.0.0-alpha.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/commands/chat.js +67 -16
- package/commands/init.js +3 -3
- package/commands/status.js +16 -1
- package/commands/up.js +119 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ crosstalk chat --shell
|
|
|
36
36
|
exit
|
|
37
37
|
crosstalk chat --agent claude --login
|
|
38
38
|
# Auth URL opens in your default browser via xdg-open / open / start.
|
|
39
|
-
# Operator never has to copy-paste it. Token saved in the /root
|
|
39
|
+
# Operator never has to copy-paste it. Token saved in the /crosstalk-root
|
|
40
40
|
# volume so subsequent dispatched workers reuse it.
|
|
41
41
|
|
|
42
42
|
# 4. Send your first message.
|
|
@@ -73,7 +73,7 @@ crosstalk replies <relPath printed by step above>
|
|
|
73
73
|
## What lives where
|
|
74
74
|
|
|
75
75
|
- **Host** — `crosstalk` binary on PATH, `~/path/to/mytransport/` (the operator's transport directory, bind-mounted).
|
|
76
|
-
- **Container** (`crosstalk-server`, image `ghcr.io/cordfuse/crosstalk-server`) — `crosstalkd` daemon, dispatch state
|
|
76
|
+
- **Container** (`crosstalk-server`, image `ghcr.io/cordfuse/crosstalk-server`) — `crosstalkd` daemon, dispatch state bind-mounted at `<base>/<transport>/crosstalk-state:/var/lib/crosstalk-state`, operator-installed agent CLIs + auth tokens bind-mounted at `<base>/<transport>/crosstalk-root:/crosstalk-root`. `<base>` is per-OS (`$XDG_DATA_HOME/crosstalk` on Linux, `~/Library/Application Support/crosstalk` on macOS, `%LOCALAPPDATA%\crosstalk` on Windows); `CROSSTALK_STORAGE_MODE=system` switches to `/var/lib`-style paths instead.
|
|
77
77
|
- **Engine HTTP API** — `127.0.0.1:7000` (compose `127.0.0.1:HOST:CONTAINER` port mapping is the actual security boundary; engine binds 0.0.0.0 inside the container).
|
|
78
78
|
- **Transport bind-mount** — `$PWD:/var/lib/crosstalk-transport`. Operator git commands on the host are seen by the engine immediately.
|
|
79
79
|
|
package/commands/chat.js
CHANGED
|
@@ -28,10 +28,59 @@ import { api, ConnectError } from '../lib/api-client.js';
|
|
|
28
28
|
import { flag, has } from '../lib/argv.js';
|
|
29
29
|
|
|
30
30
|
const KNOWN_AGENTS = ['claude', 'codex', 'gemini', 'qwen', 'opencode', 'agy'];
|
|
31
|
-
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
|
|
31
|
+
|
|
32
|
+
// URL extraction that survives agent TUI rendering. Naive regex against
|
|
33
|
+
// raw stdout fails because agent CLIs (Claude in particular) draw a box
|
|
34
|
+
// around the URL with line wraps every ~N chars; the captured URL is
|
|
35
|
+
// truncated at the first wrap, with ANSI control chars leaking in at
|
|
36
|
+
// the cut point. Real example from alpha.3 macOS test: extracted URL
|
|
37
|
+
// ended with `%18%6b…` (CAN char + box-drawing artifact) instead of
|
|
38
|
+
// the `redirect_uri=…` param, breaking OAuth.
|
|
39
|
+
//
|
|
40
|
+
// Approach:
|
|
41
|
+
// 1. ANSI-strip everything (CSI / OSC / SS3 escapes).
|
|
42
|
+
// 2. Strip control chars (0x00-0x1F except LF, plus DEL).
|
|
43
|
+
// 3. Join line wraps that occur INSIDE a URL-safe character run —
|
|
44
|
+
// `<url-char>\n<url-char>` → `<url-char><url-char>`. Preserves real
|
|
45
|
+
// `<url-char>\n<non-url-char>` boundaries.
|
|
46
|
+
// 4. Match `https?://<url-chars>+` against the joined buffer.
|
|
47
|
+
// 5. Only fire when the character AFTER the matched URL is a true
|
|
48
|
+
// terminator (whitespace, end-of-buffer) — guards against acting
|
|
49
|
+
// on a partial URL still being written.
|
|
50
|
+
|
|
51
|
+
const URL_SAFE_CLASS = "a-zA-Z0-9%/?#&=+\\-._~:@!$()*,;";
|
|
52
|
+
const URL_RE = new RegExp(`https?://[${URL_SAFE_CLASS}]+`);
|
|
53
|
+
|
|
54
|
+
function extractCompleteUrl(rawBuffer) {
|
|
55
|
+
// Strip ANSI CSI escapes (\x1B[ ... terminator).
|
|
56
|
+
let clean = rawBuffer.replace(/\x1B\[[0-9;?]*[a-zA-Z]/g, '');
|
|
57
|
+
// Strip ANSI OSC escapes (\x1B] ... BEL or ST).
|
|
58
|
+
clean = clean.replace(/\x1B\][^\x07\x1B]*(?:\x07|\x1B\\)/g, '');
|
|
59
|
+
// Strip SS3 escapes (\x1BO + one char).
|
|
60
|
+
clean = clean.replace(/\x1BO./g, '');
|
|
61
|
+
// Strip remaining single-char escapes that slipped through.
|
|
62
|
+
clean = clean.replace(/\x1B[^a-zA-Z0-9]/g, '');
|
|
63
|
+
// Strip control chars except LF (0x0A); we use LF for line-wrap rejoin.
|
|
64
|
+
clean = clean.replace(/[\x00-\x09\x0B-\x1F\x7F]/g, '');
|
|
65
|
+
// Rejoin URL-character wrap points: `<url-char>\n<url-char>` becomes
|
|
66
|
+
// `<url-char><url-char>`. Doesn't merge `<url-char>\n<space>` etc.
|
|
67
|
+
const safeRe = new RegExp(`[${URL_SAFE_CLASS}]\\n[${URL_SAFE_CLASS}]`, 'g');
|
|
68
|
+
let prev;
|
|
69
|
+
do {
|
|
70
|
+
prev = clean;
|
|
71
|
+
clean = clean.replace(safeRe, (m) => m[0] + m[2]);
|
|
72
|
+
} while (clean !== prev);
|
|
73
|
+
|
|
74
|
+
const match = clean.match(URL_RE);
|
|
75
|
+
if (!match) return null;
|
|
76
|
+
// Require a terminator after the URL — without it, the URL may still
|
|
77
|
+
// be growing in subsequent stdout chunks. Terminator = whitespace,
|
|
78
|
+
// quote/angle, or end-of-buffer-with-trailing-newline.
|
|
79
|
+
const after = clean.slice(match.index + match[0].length);
|
|
80
|
+
if (after.length === 0) return null; // EOF-as-grow case, wait
|
|
81
|
+
if (!/^[\s<>"']/.test(after)) return null; // URL still continuing
|
|
82
|
+
return match[0];
|
|
83
|
+
}
|
|
35
84
|
|
|
36
85
|
function openInBrowser(url) {
|
|
37
86
|
const cmd =
|
|
@@ -107,27 +156,29 @@ function runLogin(container, agent) {
|
|
|
107
156
|
//
|
|
108
157
|
// No agent-specific "login" subcommand — we just run the agent's
|
|
109
158
|
// default interactive entry point. First-run auth flow happens
|
|
110
|
-
// automatically for every agent we support.
|
|
111
|
-
//
|
|
159
|
+
// automatically for every agent we support.
|
|
160
|
+
//
|
|
161
|
+
// No COLUMNS=1000 env: making the agent render the URL "unwrapped" via
|
|
162
|
+
// a wide terminal claim doesn't reliably propagate to the in-container
|
|
163
|
+
// PTY, and even when it does Claude's TUI hard-wraps anyway. The
|
|
164
|
+
// extractCompleteUrl() function joins wrapped URL fragments on the
|
|
165
|
+
// host side, so the agent's rendering width is irrelevant. As bonus,
|
|
166
|
+
// dropping COLUMNS=1000 fixes daily-chat soft-wrap weirdness in
|
|
167
|
+
// long-form prose (agents see the real host terminal width).
|
|
112
168
|
return new Promise((resolve) => {
|
|
113
169
|
const child = spawn(
|
|
114
170
|
'docker',
|
|
115
|
-
[
|
|
116
|
-
'exec', '-it',
|
|
117
|
-
'--env', 'COLUMNS=1000', // fallback for browser-open failure
|
|
118
|
-
'--env', 'LINES=40',
|
|
119
|
-
container,
|
|
120
|
-
agent,
|
|
121
|
-
],
|
|
171
|
+
['exec', '-it', container, agent],
|
|
122
172
|
{ stdio: ['inherit', 'pipe', 'inherit'] },
|
|
123
173
|
);
|
|
124
174
|
let opened = false;
|
|
175
|
+
let buffer = '';
|
|
125
176
|
child.stdout.on('data', (chunk) => {
|
|
126
177
|
process.stdout.write(chunk);
|
|
127
178
|
if (opened) return;
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
179
|
+
buffer += chunk.toString();
|
|
180
|
+
const url = extractCompleteUrl(buffer);
|
|
181
|
+
if (!url) return;
|
|
131
182
|
opened = true;
|
|
132
183
|
const ok = openInBrowser(url);
|
|
133
184
|
process.stdout.write(
|
package/commands/init.js
CHANGED
|
@@ -21,7 +21,7 @@ import { spawnSync } from 'child_process';
|
|
|
21
21
|
import { has, positionals } from '../lib/argv.js';
|
|
22
22
|
|
|
23
23
|
const DEFAULT_IMAGE = process.env.CROSSTALK_IMAGE
|
|
24
|
-
?? 'ghcr.io/cordfuse/crosstalk-server:7.0.0-alpha.
|
|
24
|
+
?? 'ghcr.io/cordfuse/crosstalk-server:7.0.0-alpha.5';
|
|
25
25
|
|
|
26
26
|
function usage(exit = 0) {
|
|
27
27
|
const w = exit === 0 ? process.stdout : process.stderr;
|
|
@@ -34,9 +34,9 @@ function usage(exit = 0) {
|
|
|
34
34
|
|
|
35
35
|
The image must be pullable (or already present locally). For local dev
|
|
36
36
|
before the GHCR publish pipeline ships, build the image first:
|
|
37
|
-
docker build -t crosstalk-server:7.0.0-alpha.
|
|
37
|
+
docker build -t crosstalk-server:7.0.0-alpha.5 -f server/Dockerfile .
|
|
38
38
|
Then run:
|
|
39
|
-
CROSSTALK_IMAGE=crosstalk-server:7.0.0-alpha.
|
|
39
|
+
CROSSTALK_IMAGE=crosstalk-server:7.0.0-alpha.5 crosstalk init mytransport
|
|
40
40
|
`,
|
|
41
41
|
);
|
|
42
42
|
process.exit(exit);
|
package/commands/status.js
CHANGED
|
@@ -18,7 +18,22 @@ export async function run(_argv) {
|
|
|
18
18
|
|
|
19
19
|
process.stdout.write(`Claimed models: ${s.claimed_models.length}\n`);
|
|
20
20
|
if (s.claimed_models.length === 0) {
|
|
21
|
-
|
|
21
|
+
// Two failure shapes look the same in claimed_models=0: (a) no CLIs
|
|
22
|
+
// installed at all, (b) CLIs installed but data/models.yaml has no
|
|
23
|
+
// entries referencing them. Ask /agents/installed to distinguish so
|
|
24
|
+
// the operator gets a directly-actionable hint.
|
|
25
|
+
let installed = null;
|
|
26
|
+
try {
|
|
27
|
+
const r = await api.get('/agents/installed');
|
|
28
|
+
installed = r.installed;
|
|
29
|
+
} catch { /* ignore — fall back to generic message */ }
|
|
30
|
+
if (installed && installed.length > 0) {
|
|
31
|
+
process.stdout.write(
|
|
32
|
+
` (${installed.join(', ')} installed in the container but no data/models.yaml entry references them — add one to claim)\n`,
|
|
33
|
+
);
|
|
34
|
+
} else {
|
|
35
|
+
process.stdout.write(' (no model CLIs found inside the container — install one via `crosstalk chat --shell` and add an entry to data/models.yaml)\n');
|
|
36
|
+
}
|
|
22
37
|
} else {
|
|
23
38
|
for (const m of s.claimed_models) process.stdout.write(` - ${m}\n`);
|
|
24
39
|
}
|
package/commands/up.js
CHANGED
|
@@ -9,15 +9,17 @@
|
|
|
9
9
|
//
|
|
10
10
|
// Subsequent runs: just shell out to `docker compose up -d`.
|
|
11
11
|
|
|
12
|
-
import { writeFileSync, existsSync, appendFileSync, readFileSync } from 'fs';
|
|
12
|
+
import { writeFileSync, existsSync, mkdirSync, appendFileSync, readFileSync } from 'fs';
|
|
13
13
|
import { spawnSync } from 'child_process';
|
|
14
14
|
import { join } from 'path';
|
|
15
|
+
import { homedir } from 'os';
|
|
15
16
|
import { requireTransportRoot, transportName, composeFile } from '../lib/transport.js';
|
|
16
17
|
import { has } from '../lib/argv.js';
|
|
17
18
|
|
|
18
19
|
const DEFAULT_IMAGE = process.env.CROSSTALK_IMAGE
|
|
19
|
-
?? 'ghcr.io/cordfuse/crosstalk-server:7.0.0-alpha.
|
|
20
|
+
?? 'ghcr.io/cordfuse/crosstalk-server:7.0.0-alpha.5';
|
|
20
21
|
const DEFAULT_API_PORT = Number(process.env.CROSSTALK_API_PORT) || 7000;
|
|
22
|
+
const STORAGE_MODE = (process.env.CROSSTALK_STORAGE_MODE ?? 'user').toLowerCase();
|
|
21
23
|
|
|
22
24
|
function usage(exit = 0) {
|
|
23
25
|
const w = exit === 0 ? process.stdout : process.stderr;
|
|
@@ -26,25 +28,94 @@ function usage(exit = 0) {
|
|
|
26
28
|
|
|
27
29
|
Brings up the engine container for the transport in the current directory
|
|
28
30
|
(or any ancestor containing CROSSTALK-VERSION). First-time use generates
|
|
29
|
-
docker-compose.yml in the transport root.
|
|
30
|
-
|
|
31
|
+
docker-compose.yml in the transport root.
|
|
32
|
+
|
|
33
|
+
Environment:
|
|
34
|
+
CROSSTALK_IMAGE Image to pin (default: bundled GHCR tag)
|
|
35
|
+
CROSSTALK_API_PORT Host port to map (default: 7000)
|
|
36
|
+
CROSSTALK_ALIAS Dispatcher's machine identity in the bus
|
|
37
|
+
CROSSTALK_STORAGE_MODE user (default) | system
|
|
38
|
+
Where bind-mounted crosstalk-root +
|
|
39
|
+
crosstalk-state live on the host. 'system'
|
|
40
|
+
uses paths under /var/lib (Linux) / /Library
|
|
41
|
+
(macOS) / C:\\ProgramData (Windows) — requires
|
|
42
|
+
sudo/UAC and (on macOS) Docker Desktop file-
|
|
43
|
+
sharing allowlist. 'user' uses paths under
|
|
44
|
+
the operator's home directory with no
|
|
45
|
+
escalation.
|
|
31
46
|
`,
|
|
32
47
|
);
|
|
33
48
|
process.exit(exit);
|
|
34
49
|
}
|
|
35
50
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
51
|
+
// Resolve the host directories that back the container's /crosstalk-root
|
|
52
|
+
// (operator-installed CLIs + auth) and /var/lib/crosstalk-state
|
|
53
|
+
// (dispatcher state) bind mounts.
|
|
54
|
+
//
|
|
55
|
+
// User-mode (default) keeps everything under the operator's home so no
|
|
56
|
+
// sudo / UAC / Docker Desktop file-sharing allowlist gymnastics are
|
|
57
|
+
// needed. System-mode is for headless production-style deployments
|
|
58
|
+
// where multiple operators share a machine and a service user manages
|
|
59
|
+
// the daemon.
|
|
60
|
+
//
|
|
61
|
+
// Host-side dir names use the same `crosstalk-` prefix as the container
|
|
62
|
+
// paths so things stay identifiable wherever they surface (backup tools,
|
|
63
|
+
// file managers, support-email screenshots).
|
|
64
|
+
function resolveStoragePaths(transportName, mode) {
|
|
65
|
+
const platform = process.platform;
|
|
66
|
+
// Per-OS base directories.
|
|
67
|
+
const bases = (platform === 'darwin')
|
|
68
|
+
? {
|
|
69
|
+
user: join(homedir(), 'Library', 'Application Support', 'crosstalk'),
|
|
70
|
+
system: '/Library/Application Support/crosstalk',
|
|
71
|
+
}
|
|
72
|
+
: (platform === 'win32')
|
|
73
|
+
? {
|
|
74
|
+
// %LOCALAPPDATA% should always be set on supported Win versions;
|
|
75
|
+
// fall back to ~/AppData/Local if it isn't.
|
|
76
|
+
user: process.env['LOCALAPPDATA']
|
|
77
|
+
? join(process.env['LOCALAPPDATA'], 'crosstalk')
|
|
78
|
+
: join(homedir(), 'AppData', 'Local', 'crosstalk'),
|
|
79
|
+
system: 'C:\\ProgramData\\crosstalk',
|
|
80
|
+
}
|
|
81
|
+
: /* linux + other unix */ {
|
|
82
|
+
// Honor XDG_DATA_HOME silently (no docs surface; the 99% of
|
|
83
|
+
// operators who haven't set it get ~/.local/share/crosstalk
|
|
84
|
+
// either way).
|
|
85
|
+
user: join(process.env['XDG_DATA_HOME'] ?? join(homedir(), '.local', 'share'), 'crosstalk'),
|
|
86
|
+
system: '/var/lib/crosstalk',
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
if (mode !== 'user' && mode !== 'system') {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`crosstalk up: CROSSTALK_STORAGE_MODE='${mode}' invalid — must be 'user' or 'system'.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const base = bases[mode];
|
|
95
|
+
const transportRoot = join(base, transportName);
|
|
96
|
+
return {
|
|
97
|
+
crosstalkRoot: join(transportRoot, 'crosstalk-root'),
|
|
98
|
+
crosstalkState: join(transportRoot, 'crosstalk-state'),
|
|
99
|
+
mode,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function renderCompose({ name, image, apiPort, alias, uid, gid, crosstalkRootHost, crosstalkStateHost, storageMode }) {
|
|
104
|
+
// Entrypoint runs as root for setup (npm prefix, SSH key import). It
|
|
105
|
+
// then adjusts a 'crosstalkd' user to match CROSSTALK_UID/GID
|
|
106
|
+
// (operator's host UID), chowns the bind-mounted transport, state
|
|
107
|
+
// dir, AND /crosstalk-root to that user, then drops via setpriv before
|
|
108
|
+
// exec'ing the dispatcher. Net effect: bind-mount files written by
|
|
109
|
+
// the engine are operator-owned on the host, so \`git remote add\`
|
|
110
|
+
// and \`rm -rf\` from the host both work without sudo.
|
|
45
111
|
return `# Generated by \`crosstalk up\`. Safe to edit; subsequent \`up\`s
|
|
46
112
|
# only regenerate when this file is missing. Compose config is machine-
|
|
47
113
|
# local — kept out of git via .gitignore by default.
|
|
114
|
+
#
|
|
115
|
+
# Storage mode: ${storageMode}
|
|
116
|
+
# crosstalk-root (container /crosstalk-root) → ${crosstalkRootHost}
|
|
117
|
+
# crosstalk-state (container /var/lib/crosstalk-state) → ${crosstalkStateHost}
|
|
118
|
+
# transport (container /var/lib/crosstalk-transport) → \${PWD} (bind to this directory)
|
|
48
119
|
|
|
49
120
|
services:
|
|
50
121
|
crosstalkd:
|
|
@@ -58,11 +129,12 @@ services:
|
|
|
58
129
|
# to the engine on the next tick. No clone-on-startup needed.
|
|
59
130
|
- .:/var/lib/crosstalk-transport
|
|
60
131
|
# Operator-installed agent CLIs (claude, codex, agy, ...) + auth
|
|
61
|
-
# state.
|
|
62
|
-
-
|
|
63
|
-
|
|
64
|
-
#
|
|
65
|
-
|
|
132
|
+
# state. Chowned to operator UID at entrypoint so host-side
|
|
133
|
+
# \`rm -rf\` works without sudo.
|
|
134
|
+
- ${crosstalkRootHost}:/crosstalk-root
|
|
135
|
+
# Dispatcher's machine-local state: cursor, heartbeat, errors.log,
|
|
136
|
+
# pidfile, wake.signal. Operator-UID-owned (chowned by entrypoint).
|
|
137
|
+
- ${crosstalkStateHost}:/var/lib/crosstalk-state
|
|
66
138
|
environment:
|
|
67
139
|
CROSSTALK_ALIAS: ${alias}
|
|
68
140
|
CROSSTALK_UID: "${uid}"
|
|
@@ -70,10 +142,6 @@ services:
|
|
|
70
142
|
CROSSTALKD_API_PORT: "7000"
|
|
71
143
|
DISPATCH_JSON: "true"
|
|
72
144
|
DISPATCH_POLL_SECONDS: "30"
|
|
73
|
-
|
|
74
|
-
volumes:
|
|
75
|
-
crosstalk-root:
|
|
76
|
-
crosstalk-state:
|
|
77
145
|
`;
|
|
78
146
|
}
|
|
79
147
|
|
|
@@ -101,6 +169,31 @@ export async function run(argv) {
|
|
|
101
169
|
const alias = process.env.CROSSTALK_ALIAS ?? process.env.HOSTNAME ?? transportName(root);
|
|
102
170
|
const uid = typeof process.getuid === 'function' ? process.getuid() : 1000;
|
|
103
171
|
const gid = typeof process.getgid === 'function' ? process.getgid() : 1000;
|
|
172
|
+
let storage;
|
|
173
|
+
try {
|
|
174
|
+
storage = resolveStoragePaths(transportName(root), STORAGE_MODE);
|
|
175
|
+
} catch (err) {
|
|
176
|
+
process.stderr.write(`${err.message}\n`);
|
|
177
|
+
return 1;
|
|
178
|
+
}
|
|
179
|
+
// Pre-create the host bind-mount directories. Docker would auto-
|
|
180
|
+
// create them as root-owned otherwise, which would break the
|
|
181
|
+
// operator-UID chown step inside the container.
|
|
182
|
+
try {
|
|
183
|
+
mkdirSync(storage.crosstalkRoot, { recursive: true });
|
|
184
|
+
mkdirSync(storage.crosstalkState, { recursive: true });
|
|
185
|
+
} catch (err) {
|
|
186
|
+
process.stderr.write(
|
|
187
|
+
`crosstalk up: failed to create storage directories — ${err.message}\n`,
|
|
188
|
+
);
|
|
189
|
+
if (storage.mode === 'system') {
|
|
190
|
+
process.stderr.write(
|
|
191
|
+
` System-mode storage requires write access to ${storage.crosstalkRoot}'s parent.\n` +
|
|
192
|
+
` Try: sudo mkdir -p ${storage.crosstalkRoot} ${storage.crosstalkState} && sudo chown -R \$USER ${storage.crosstalkRoot} ${storage.crosstalkState}\n`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
return 1;
|
|
196
|
+
}
|
|
104
197
|
const content = renderCompose({
|
|
105
198
|
name: transportName(root),
|
|
106
199
|
image: DEFAULT_IMAGE,
|
|
@@ -108,10 +201,13 @@ export async function run(argv) {
|
|
|
108
201
|
alias,
|
|
109
202
|
uid,
|
|
110
203
|
gid,
|
|
204
|
+
crosstalkRootHost: storage.crosstalkRoot,
|
|
205
|
+
crosstalkStateHost: storage.crosstalkState,
|
|
206
|
+
storageMode: storage.mode,
|
|
111
207
|
});
|
|
112
208
|
writeFileSync(composeYml, content);
|
|
113
209
|
ensureGitignored(root);
|
|
114
|
-
process.stdout.write(`Generated ${composeYml}\n`);
|
|
210
|
+
process.stdout.write(`Generated ${composeYml} (storage: ${storage.mode})\n`);
|
|
115
211
|
}
|
|
116
212
|
|
|
117
213
|
const r = spawnSync('docker', ['compose', '-f', composeYml, 'up', '-d'], {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cordfuse/crosstalk",
|
|
3
|
-
"version": "7.0.0-alpha.
|
|
3
|
+
"version": "7.0.0-alpha.5",
|
|
4
4
|
"description": "Crosstalk client — host-side CLI that talks to the crosstalkd daemon running inside the crosstalk-server container.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|