@dmsdc-ai/aigentry-telepty 0.1.75 → 0.1.77

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dmsdc-ai/aigentry-telepty",
3
- "version": "0.1.75",
3
+ "version": "0.1.77",
4
4
  "main": "daemon.js",
5
5
  "bin": {
6
6
  "aigentry-telepty": "install.js",
@@ -38,11 +38,13 @@ function pickSessionTarget(sessionRef, sessions, defaultHost = '127.0.0.1') {
38
38
  }
39
39
 
40
40
  if (parsed.host) {
41
- return { id: parsed.id, host: parsed.host };
41
+ const exactMatch = sessions.find((session) => session.id === parsed.id && session.host === parsed.host);
42
+ return exactMatch || { id: parsed.id, host: parsed.host };
42
43
  }
43
44
 
44
45
  if (defaultHost && defaultHost !== '127.0.0.1') {
45
- return { id: parsed.id, host: defaultHost };
46
+ const defaultMatch = sessions.find((session) => session.id === parsed.id && session.host === defaultHost);
47
+ return defaultMatch || { id: parsed.id, host: defaultHost };
46
48
  }
47
49
 
48
50
  const matches = sessions.filter((session) => session.id === parsed.id);
@@ -54,19 +56,19 @@ function pickSessionTarget(sessionRef, sessions, defaultHost = '127.0.0.1') {
54
56
  s.id.startsWith(parsed.id + '-') || s.id.startsWith(parsed.id)
55
57
  );
56
58
  if (prefixMatches.length === 1) {
57
- return { id: prefixMatches[0].id, host: prefixMatches[0].host };
59
+ return prefixMatches[0];
58
60
  }
59
61
  if (prefixMatches.length > 1) {
60
62
  // Multiple candidates: prefer same host, then first alphabetically
61
63
  const local = prefixMatches.find((s) => s.host === '127.0.0.1');
62
64
  const best = local || prefixMatches.sort((a, b) => a.id.localeCompare(b.id))[0];
63
- return { id: best.id, host: best.host };
65
+ return best;
64
66
  }
65
67
  return null;
66
68
  }
67
69
 
68
70
  if (matches.length === 1) {
69
- return { id: parsed.id, host: matches[0].host };
71
+ return matches[0];
70
72
  }
71
73
 
72
74
  const hosts = matches.map((session) => session.host).sort().join(', ');
@@ -0,0 +1,147 @@
1
+ 'use strict';
2
+
3
+ const crypto = require('crypto');
4
+ const fs = require('fs');
5
+ const os = require('os');
6
+ const path = require('path');
7
+
8
+ const DEFAULT_SHARED_REF_TTL_DAYS = 7;
9
+ const SHARED_REF_DIR_SEGMENTS = ['.telepty', 'shared'];
10
+
11
+ function getSharedContextDir(homeDir = os.homedir()) {
12
+ return path.join(homeDir, ...SHARED_REF_DIR_SEGMENTS);
13
+ }
14
+
15
+ function getSharedContextPromptPath(fileName) {
16
+ return path.posix.join('~', ...SHARED_REF_DIR_SEGMENTS, fileName);
17
+ }
18
+
19
+ function getSharedContextTtlMs(env = process.env) {
20
+ const rawDays = env.TELEPTY_SHARED_REF_TTL_DAYS;
21
+ if (rawDays == null || rawDays === '') {
22
+ return DEFAULT_SHARED_REF_TTL_DAYS * 24 * 60 * 60 * 1000;
23
+ }
24
+
25
+ const days = Number(rawDays);
26
+ if (!Number.isFinite(days) || days < 0) {
27
+ return DEFAULT_SHARED_REF_TTL_DAYS * 24 * 60 * 60 * 1000;
28
+ }
29
+
30
+ return days * 24 * 60 * 60 * 1000;
31
+ }
32
+
33
+ function createSharedContextDescriptor(content) {
34
+ const normalized = String(content ?? '');
35
+ if (!normalized.trim()) {
36
+ throw new Error('Shared reference content cannot be empty.');
37
+ }
38
+
39
+ const hash = crypto.createHash('sha256').update(normalized).digest('hex');
40
+ const fileName = `${hash}.md`;
41
+
42
+ return {
43
+ content: normalized,
44
+ hash,
45
+ fileName,
46
+ promptPath: getSharedContextPromptPath(fileName)
47
+ };
48
+ }
49
+
50
+ function cleanupSharedContextFiles(options = {}) {
51
+ const dir = options.dir || getSharedContextDir(options.homeDir);
52
+ const ttlMs = options.ttlMs ?? getSharedContextTtlMs(options.env);
53
+ if (!Number.isFinite(ttlMs) || ttlMs <= 0) {
54
+ return { removed: 0 };
55
+ }
56
+
57
+ let entries;
58
+ try {
59
+ entries = fs.readdirSync(dir, { withFileTypes: true });
60
+ } catch (error) {
61
+ if (error.code === 'ENOENT') {
62
+ return { removed: 0 };
63
+ }
64
+ throw error;
65
+ }
66
+
67
+ const cutoff = (options.now ?? Date.now()) - ttlMs;
68
+ let removed = 0;
69
+
70
+ for (const entry of entries) {
71
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
72
+ continue;
73
+ }
74
+
75
+ const entryPath = path.join(dir, entry.name);
76
+ let stat;
77
+ try {
78
+ stat = fs.statSync(entryPath);
79
+ } catch {
80
+ continue;
81
+ }
82
+
83
+ if (stat.mtimeMs >= cutoff) {
84
+ continue;
85
+ }
86
+
87
+ try {
88
+ fs.unlinkSync(entryPath);
89
+ removed += 1;
90
+ } catch {
91
+ // Ignore best-effort cleanup failures.
92
+ }
93
+ }
94
+
95
+ return { removed };
96
+ }
97
+
98
+ function ensureSharedContextFile(descriptor, options = {}) {
99
+ const dir = options.dir || getSharedContextDir(options.homeDir);
100
+ cleanupSharedContextFiles({ dir, ttlMs: options.ttlMs, env: options.env, now: options.now });
101
+ fs.mkdirSync(dir, { recursive: true });
102
+
103
+ const filePath = path.join(dir, descriptor.fileName);
104
+ let created = false;
105
+
106
+ if (!fs.existsSync(filePath)) {
107
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
108
+ fs.writeFileSync(tempPath, descriptor.content, { encoding: 'utf8', mode: 0o600 });
109
+ try {
110
+ fs.renameSync(tempPath, filePath);
111
+ created = true;
112
+ } catch (error) {
113
+ if (fs.existsSync(filePath)) {
114
+ try {
115
+ fs.unlinkSync(tempPath);
116
+ } catch {
117
+ // Ignore temp cleanup failures when another process won the race.
118
+ }
119
+ } else {
120
+ throw error;
121
+ }
122
+ }
123
+ }
124
+
125
+ return {
126
+ ...descriptor,
127
+ filePath,
128
+ created
129
+ };
130
+ }
131
+
132
+ function buildSharedContextPrompt(descriptorOrPath) {
133
+ const promptPath = typeof descriptorOrPath === 'string'
134
+ ? descriptorOrPath
135
+ : descriptorOrPath.promptPath;
136
+ return `[context-ref] Read ${promptPath} and use it as the source of truth for this task.`;
137
+ }
138
+
139
+ module.exports = {
140
+ buildSharedContextPrompt,
141
+ cleanupSharedContextFiles,
142
+ createSharedContextDescriptor,
143
+ ensureSharedContextFile,
144
+ getSharedContextDir,
145
+ getSharedContextPromptPath,
146
+ getSharedContextTtlMs
147
+ };