pty-auto 0.3.6

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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +515 -0
  3. package/dist/index.d.ts +3 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +3 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/plugin/constants.d.ts +5 -0
  8. package/dist/src/plugin/constants.d.ts.map +1 -0
  9. package/dist/src/plugin/constants.js +7 -0
  10. package/dist/src/plugin/constants.js.map +1 -0
  11. package/dist/src/plugin/pty/buffer.d.ts +19 -0
  12. package/dist/src/plugin/pty/buffer.d.ts.map +1 -0
  13. package/dist/src/plugin/pty/buffer.js +61 -0
  14. package/dist/src/plugin/pty/buffer.js.map +1 -0
  15. package/dist/src/plugin/pty/formatters.d.ts +4 -0
  16. package/dist/src/plugin/pty/formatters.d.ts.map +1 -0
  17. package/dist/src/plugin/pty/formatters.js +22 -0
  18. package/dist/src/plugin/pty/formatters.js.map +1 -0
  19. package/dist/src/plugin/pty/manager.d.ts +33 -0
  20. package/dist/src/plugin/pty/manager.d.ts.map +1 -0
  21. package/dist/src/plugin/pty/manager.js +108 -0
  22. package/dist/src/plugin/pty/manager.js.map +1 -0
  23. package/dist/src/plugin/pty/notification-manager.d.ts +9 -0
  24. package/dist/src/plugin/pty/notification-manager.d.ts.map +1 -0
  25. package/dist/src/plugin/pty/notification-manager.js +69 -0
  26. package/dist/src/plugin/pty/notification-manager.js.map +1 -0
  27. package/dist/src/plugin/pty/output-manager.d.ts +7 -0
  28. package/dist/src/plugin/pty/output-manager.d.ts.map +1 -0
  29. package/dist/src/plugin/pty/output-manager.js +26 -0
  30. package/dist/src/plugin/pty/output-manager.js.map +1 -0
  31. package/dist/src/plugin/pty/permissions.d.ts +27 -0
  32. package/dist/src/plugin/pty/permissions.d.ts.map +1 -0
  33. package/dist/src/plugin/pty/permissions.js +54 -0
  34. package/dist/src/plugin/pty/permissions.js.map +1 -0
  35. package/dist/src/plugin/pty/session-lifecycle.d.ts +20 -0
  36. package/dist/src/plugin/pty/session-lifecycle.d.ts.map +1 -0
  37. package/dist/src/plugin/pty/session-lifecycle.js +177 -0
  38. package/dist/src/plugin/pty/session-lifecycle.js.map +1 -0
  39. package/dist/src/plugin/pty/tools/kill.d.ts +12 -0
  40. package/dist/src/plugin/pty/tools/kill.d.ts.map +1 -0
  41. package/dist/src/plugin/pty/tools/kill.js +37 -0
  42. package/dist/src/plugin/pty/tools/kill.js.map +1 -0
  43. package/dist/src/plugin/pty/tools/kill.txt +25 -0
  44. package/dist/src/plugin/pty/tools/list.d.ts +6 -0
  45. package/dist/src/plugin/pty/tools/list.d.ts.map +1 -0
  46. package/dist/src/plugin/pty/tools/list.js +22 -0
  47. package/dist/src/plugin/pty/tools/list.js.map +1 -0
  48. package/dist/src/plugin/pty/tools/list.txt +22 -0
  49. package/dist/src/plugin/pty/tools/read.d.ts +18 -0
  50. package/dist/src/plugin/pty/tools/read.d.ts.map +1 -0
  51. package/dist/src/plugin/pty/tools/read.js +165 -0
  52. package/dist/src/plugin/pty/tools/read.js.map +1 -0
  53. package/dist/src/plugin/pty/tools/read.txt +39 -0
  54. package/dist/src/plugin/pty/tools/spawn.d.ts +24 -0
  55. package/dist/src/plugin/pty/tools/spawn.d.ts.map +1 -0
  56. package/dist/src/plugin/pty/tools/spawn.js +75 -0
  57. package/dist/src/plugin/pty/tools/spawn.js.map +1 -0
  58. package/dist/src/plugin/pty/tools/spawn.txt +52 -0
  59. package/dist/src/plugin/pty/tools/write.d.ts +12 -0
  60. package/dist/src/plugin/pty/tools/write.d.ts.map +1 -0
  61. package/dist/src/plugin/pty/tools/write.js +62 -0
  62. package/dist/src/plugin/pty/tools/write.js.map +1 -0
  63. package/dist/src/plugin/pty/tools/write.txt +28 -0
  64. package/dist/src/plugin/pty/types.d.ts +70 -0
  65. package/dist/src/plugin/pty/types.d.ts.map +1 -0
  66. package/dist/src/plugin/pty/types.js +1 -0
  67. package/dist/src/plugin/pty/types.js.map +1 -0
  68. package/dist/src/plugin/pty/utils.d.ts +12 -0
  69. package/dist/src/plugin/pty/utils.d.ts.map +1 -0
  70. package/dist/src/plugin/pty/utils.js +17 -0
  71. package/dist/src/plugin/pty/utils.js.map +1 -0
  72. package/dist/src/plugin/pty/wildcard.d.ts +7 -0
  73. package/dist/src/plugin/pty/wildcard.d.ts.map +1 -0
  74. package/dist/src/plugin/pty/wildcard.js +51 -0
  75. package/dist/src/plugin/pty/wildcard.js.map +1 -0
  76. package/dist/src/plugin/types.d.ts +5 -0
  77. package/dist/src/plugin/types.d.ts.map +1 -0
  78. package/dist/src/plugin/types.js +1 -0
  79. package/dist/src/plugin/types.js.map +1 -0
  80. package/dist/src/plugin.d.ts +3 -0
  81. package/dist/src/plugin.d.ts.map +1 -0
  82. package/dist/src/plugin.js +71 -0
  83. package/dist/src/plugin.js.map +1 -0
  84. package/dist/src/shared/constants.d.ts +5 -0
  85. package/dist/src/shared/constants.d.ts.map +1 -0
  86. package/dist/src/shared/constants.js +7 -0
  87. package/dist/src/shared/constants.js.map +1 -0
  88. package/dist/src/web/server/callback-manager.d.ts +8 -0
  89. package/dist/src/web/server/callback-manager.d.ts.map +1 -0
  90. package/dist/src/web/server/callback-manager.js +23 -0
  91. package/dist/src/web/server/callback-manager.js.map +1 -0
  92. package/dist/src/web/server/handlers/health.d.ts +3 -0
  93. package/dist/src/web/server/handlers/health.d.ts.map +1 -0
  94. package/dist/src/web/server/handlers/health.js +33 -0
  95. package/dist/src/web/server/handlers/health.js.map +1 -0
  96. package/dist/src/web/server/handlers/responses.d.ts +10 -0
  97. package/dist/src/web/server/handlers/responses.d.ts.map +1 -0
  98. package/dist/src/web/server/handlers/responses.js +26 -0
  99. package/dist/src/web/server/handlers/responses.js.map +1 -0
  100. package/dist/src/web/server/handlers/sessions.d.ts +13 -0
  101. package/dist/src/web/server/handlers/sessions.d.ts.map +1 -0
  102. package/dist/src/web/server/handlers/sessions.js +96 -0
  103. package/dist/src/web/server/handlers/sessions.js.map +1 -0
  104. package/dist/src/web/server/handlers/static.d.ts +2 -0
  105. package/dist/src/web/server/handlers/static.d.ts.map +1 -0
  106. package/dist/src/web/server/handlers/static.js +38 -0
  107. package/dist/src/web/server/handlers/static.js.map +1 -0
  108. package/dist/src/web/server/handlers/upgrade.d.ts +2 -0
  109. package/dist/src/web/server/handlers/upgrade.d.ts.map +1 -0
  110. package/dist/src/web/server/handlers/upgrade.js +11 -0
  111. package/dist/src/web/server/handlers/upgrade.js.map +1 -0
  112. package/dist/src/web/server/handlers/websocket.d.ts +3 -0
  113. package/dist/src/web/server/handlers/websocket.d.ts.map +1 -0
  114. package/dist/src/web/server/handlers/websocket.js +119 -0
  115. package/dist/src/web/server/handlers/websocket.js.map +1 -0
  116. package/dist/src/web/server/server.d.ts +12 -0
  117. package/dist/src/web/server/server.d.ts.map +1 -0
  118. package/dist/src/web/server/server.js +73 -0
  119. package/dist/src/web/server/server.js.map +1 -0
  120. package/dist/src/web/shared/api-client.d.ts +132 -0
  121. package/dist/src/web/shared/api-client.d.ts.map +1 -0
  122. package/dist/src/web/shared/api-client.js +79 -0
  123. package/dist/src/web/shared/api-client.js.map +1 -0
  124. package/dist/src/web/shared/constants.d.ts +11 -0
  125. package/dist/src/web/shared/constants.d.ts.map +1 -0
  126. package/dist/src/web/shared/constants.js +23 -0
  127. package/dist/src/web/shared/constants.js.map +1 -0
  128. package/dist/src/web/shared/route-builder.d.ts +30 -0
  129. package/dist/src/web/shared/route-builder.d.ts.map +1 -0
  130. package/dist/src/web/shared/route-builder.js +42 -0
  131. package/dist/src/web/shared/route-builder.js.map +1 -0
  132. package/dist/src/web/shared/routes.d.ts +37 -0
  133. package/dist/src/web/shared/routes.d.ts.map +1 -0
  134. package/dist/src/web/shared/routes.js +39 -0
  135. package/dist/src/web/shared/routes.js.map +1 -0
  136. package/dist/src/web/shared/types.d.ts +87 -0
  137. package/dist/src/web/shared/types.d.ts.map +1 -0
  138. package/dist/src/web/shared/types.js +15 -0
  139. package/dist/src/web/shared/types.js.map +1 -0
  140. package/dist/web/assets/index-CYWtqpTG.js +24 -0
  141. package/dist/web/assets/index-DDGTF8rc.css +1 -0
  142. package/dist/web/index.html +204 -0
  143. package/package.json +107 -0
@@ -0,0 +1,37 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import { manager } from "../manager.js";
3
+ import { buildSessionNotFoundError } from "../utils.js";
4
+ import DESCRIPTION from './kill.txt';
5
+ export const ptyKill = tool({
6
+ description: DESCRIPTION,
7
+ args: {
8
+ id: tool.schema.string().describe('The PTY session ID (e.g., pty_a1b2c3d4)'),
9
+ cleanup: tool.schema
10
+ .boolean()
11
+ .optional()
12
+ .describe('If true, removes the session and frees the buffer (default: false)'),
13
+ },
14
+ async execute(args) {
15
+ const session = manager.get(args.id);
16
+ if (!session) {
17
+ throw buildSessionNotFoundError(args.id);
18
+ }
19
+ const wasRunning = session.status === 'running';
20
+ const cleanup = args.cleanup ?? false;
21
+ const success = manager.kill(args.id, cleanup);
22
+ if (!success) {
23
+ throw new Error(`Failed to kill PTY session '${args.id}'.`);
24
+ }
25
+ const action = wasRunning ? 'Killed' : 'Cleaned up';
26
+ const cleanupNote = cleanup ? ' (session removed)' : ' (session retained for log access)';
27
+ return [
28
+ `<pty_killed>`,
29
+ `${action}: ${args.id}${cleanupNote}`,
30
+ `Title: ${session.title}`,
31
+ `Command: ${session.command} ${session.args.join(' ')}`,
32
+ `Final line count: ${session.lineCount}`,
33
+ `</pty_killed>`,
34
+ ].join('\n');
35
+ },
36
+ });
37
+ //# sourceMappingURL=kill.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kill.js","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/kill.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,WAAW,MAAM,YAAY,CAAA;AAEpC,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,WAAW;IACxB,IAAI,EAAE;QACJ,EAAE,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,yCAAyC,CAAC;QAC5E,OAAO,EAAE,IAAI,CAAC,MAAM;aACjB,OAAO,EAAE;aACT,QAAQ,EAAE;aACV,QAAQ,CAAC,oEAAoE,CAAC;KAClF;IACD,KAAK,CAAC,OAAO,CAAC,IAAI;QAChB,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QACpC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;QAC1C,CAAC;QAED,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,KAAK,SAAS,CAAA;QAC/C,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,KAAK,CAAA;QACrC,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,OAAO,CAAC,CAAA;QAE9C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,EAAE,IAAI,CAAC,CAAA;QAC7D,CAAC;QAED,MAAM,MAAM,GAAG,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAA;QACnD,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,oBAAoB,CAAC,CAAC,CAAC,oCAAoC,CAAA;QAEzF,OAAO;YACL,cAAc;YACd,GAAG,MAAM,KAAK,IAAI,CAAC,EAAE,GAAG,WAAW,EAAE;YACrC,UAAU,OAAO,CAAC,KAAK,EAAE;YACzB,YAAY,OAAO,CAAC,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YACvD,qBAAqB,OAAO,CAAC,SAAS,EAAE;YACxC,eAAe;SAChB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACd,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,25 @@
1
+ Terminates a PTY session and optionally cleans up its buffer.
2
+
3
+ Use this tool to:
4
+ - Stop a running process (sends SIGTERM)
5
+ - Clean up an exited session to free memory
6
+ - Remove a session from the list
7
+
8
+ Usage:
9
+ - `id`: The PTY session ID (from pty_spawn or pty_list)
10
+ - `cleanup`: If true, removes the session and frees the buffer (default: false)
11
+
12
+ Behavior:
13
+ - If the session is running, it will be killed (status becomes "killed")
14
+ - If cleanup=false (default), the session remains in the list with its output buffer intact
15
+ - If cleanup=true, the session is removed entirely and the buffer is freed
16
+ - Keeping sessions without cleanup allows you to compare logs between runs
17
+
18
+ Tips:
19
+ - Use cleanup=false if you might want to read the output later
20
+ - Use cleanup=true when you're done with the session entirely
21
+ - To send Ctrl+C instead of killing, use pty_write with data="\x03"
22
+
23
+ Examples:
24
+ - Kill but keep logs: cleanup=false (or omit)
25
+ - Kill and remove: cleanup=true
@@ -0,0 +1,6 @@
1
+ export declare const ptyList: {
2
+ description: string;
3
+ args: {};
4
+ execute(args: Record<string, never>, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
5
+ };
6
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/list.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,OAAO;;;;CAmBlB,CAAA"}
@@ -0,0 +1,22 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import { manager } from "../manager.js";
3
+ import { formatSessionInfo } from "../formatters.js";
4
+ import DESCRIPTION from './list.txt';
5
+ export const ptyList = tool({
6
+ description: DESCRIPTION,
7
+ args: {},
8
+ async execute() {
9
+ const sessions = manager.list();
10
+ if (sessions.length === 0) {
11
+ return '<pty_list>\nNo active PTY sessions.\n</pty_list>';
12
+ }
13
+ const lines = ['<pty_list>'];
14
+ for (const session of sessions) {
15
+ lines.push(...formatSessionInfo(session));
16
+ }
17
+ lines.push(`Total: ${sessions.length} session(s)`);
18
+ lines.push('</pty_list>');
19
+ return lines.join('\n');
20
+ },
21
+ });
22
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/list.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,qBAAqB,CAAA;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAA;AACvC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAA;AACpD,OAAO,WAAW,MAAM,YAAY,CAAA;AAEpC,MAAM,CAAC,MAAM,OAAO,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,WAAW;IACxB,IAAI,EAAE,EAAE;IACR,KAAK,CAAC,OAAO;QACX,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,EAAE,CAAA;QAE/B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,kDAAkD,CAAA;QAC3D,CAAC;QAED,MAAM,KAAK,GAAG,CAAC,YAAY,CAAC,CAAA;QAC5B,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAA;QAC3C,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,UAAU,QAAQ,CAAC,MAAM,aAAa,CAAC,CAAA;QAClD,KAAK,CAAC,IAAI,CAAC,aAAa,CAAC,CAAA;QAEzB,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACzB,CAAC;CACF,CAAC,CAAA"}
@@ -0,0 +1,22 @@
1
+ Lists all PTY sessions (active and exited).
2
+
3
+ Use this tool to:
4
+ - See all running and exited PTY sessions
5
+ - Get session IDs for use with other pty_* tools
6
+ - Check the status and output line count of each session
7
+ - Monitor which processes are still running
8
+
9
+ Returns for each session:
10
+ - `id`: Unique identifier for use with other tools
11
+ - `title`: Human-readable name
12
+ - `command`: The command that was executed
13
+ - `status`: Current status (running, exited, killed)
14
+ - `exitCode`: Exit code (if exited/killed)
15
+ - `pid`: Process ID
16
+ - `lineCount`: Number of lines in the output buffer
17
+ - `createdAt`: When the session was created
18
+
19
+ Tips:
20
+ - Use the session ID with pty_read, pty_write, or pty_kill
21
+ - Sessions remain in the list after exit until explicitly cleaned up with pty_kill
22
+ - This allows you to compare output from multiple sessions
@@ -0,0 +1,18 @@
1
+ export declare const ptyRead: {
2
+ description: string;
3
+ args: {
4
+ id: import("zod").ZodString;
5
+ offset: import("zod").ZodOptional<import("zod").ZodNumber>;
6
+ limit: import("zod").ZodOptional<import("zod").ZodNumber>;
7
+ pattern: import("zod").ZodOptional<import("zod").ZodString>;
8
+ ignoreCase: import("zod").ZodOptional<import("zod").ZodBoolean>;
9
+ };
10
+ execute(args: {
11
+ id: string;
12
+ offset?: number | undefined;
13
+ limit?: number | undefined;
14
+ pattern?: string | undefined;
15
+ ignoreCase?: boolean | undefined;
16
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
17
+ };
18
+ //# sourceMappingURL=read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/read.ts"],"names":[],"mappings":"AAoNA,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;CA0ClB,CAAA"}
@@ -0,0 +1,165 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import { manager } from "../manager.js";
3
+ import { DEFAULT_READ_LIMIT, MAX_LINE_LENGTH } from "../../../shared/constants.js";
4
+ import { buildSessionNotFoundError } from "../utils.js";
5
+ import { formatLine } from "../formatters.js";
6
+ import DESCRIPTION from './read.txt';
7
+ const NOTIFY_ON_EXIT_REMINDER = [
8
+ `<system_reminder>`,
9
+ `This session was started with \`notifyOnExit=true\`.`,
10
+ `Completion signal is the future \`<pty_exited>\` message, not repeated \`pty_read\` calls.`,
11
+ `If you only need to know whether the command finished, stop polling and wait for \`<pty_exited>\`.`,
12
+ `Do not use sleep plus \`pty_read\` loops to check completion.`,
13
+ `Use \`pty_read\` only when you need live output now, the user explicitly asks for logs, or the exit notification reports a non-zero status and you need to investigate.`,
14
+ `</system_reminder>`,
15
+ ].join('\n');
16
+ function buildTimeoutReminder(session) {
17
+ return [
18
+ `<system_reminder>`,
19
+ `This session was auto-killed after reaching \`timeoutSeconds=${session.timeoutSeconds ?? 'unknown'}\`.`,
20
+ `Use \`pty_read\` to inspect the final output or \`pty_list\` to review other sessions.`,
21
+ `</system_reminder>`,
22
+ ].join('\n');
23
+ }
24
+ /**
25
+ * Formats PTY output with XML tags and pagination
26
+ */
27
+ function formatPtyOutput(id, status, pattern, formattedLines, hasMore, paginationMessage, endMessage) {
28
+ const output = [
29
+ `<pty_output id="${id}" status="${status}"${pattern ? ` pattern="${pattern}"` : ''}>`,
30
+ ...formattedLines,
31
+ '',
32
+ hasMore ? paginationMessage : endMessage,
33
+ `</pty_output>`,
34
+ ];
35
+ return output.join('\n');
36
+ }
37
+ function appendNotifyOnExitReminder(output, session) {
38
+ if (!session.notifyOnExit || session.status !== 'running') {
39
+ return output;
40
+ }
41
+ return `${output}\n\n${NOTIFY_ON_EXIT_REMINDER}`;
42
+ }
43
+ function appendTimeoutReminder(output, session) {
44
+ if (!session.timedOut) {
45
+ return output;
46
+ }
47
+ return `${output}\n\n${buildTimeoutReminder(session)}`;
48
+ }
49
+ function appendSessionReminders(output, session) {
50
+ return appendTimeoutReminder(appendNotifyOnExitReminder(output, session), session);
51
+ }
52
+ /**
53
+ * Validates and creates a RegExp from pattern string
54
+ */
55
+ function validateAndCreateRegex(pattern, ignoreCase) {
56
+ if (!validateRegex(pattern)) {
57
+ throw new Error(`Potentially dangerous regex pattern rejected: '${pattern}'. Please use a safer pattern.`);
58
+ }
59
+ try {
60
+ return new RegExp(pattern, ignoreCase ? 'i' : '');
61
+ }
62
+ catch (e) {
63
+ const error = e instanceof Error ? e.message : String(e);
64
+ throw new Error(`Invalid regex pattern '${pattern}': ${error}`);
65
+ }
66
+ }
67
+ /**
68
+ * Handles pattern-based reading and formatting
69
+ */
70
+ function handlePatternRead(id, pattern, ignoreCase, session, offset, limit) {
71
+ const regex = validateAndCreateRegex(pattern, ignoreCase);
72
+ const result = manager.search(id, regex, offset, limit);
73
+ if (!result) {
74
+ throw buildSessionNotFoundError(id);
75
+ }
76
+ if (result.matches.length === 0) {
77
+ return appendSessionReminders([
78
+ `<pty_output id="${id}" status="${session.status}" pattern="${pattern}">`,
79
+ `No lines matched the pattern '${pattern}'.`,
80
+ `Total lines in buffer: ${result.totalLines}`,
81
+ `</pty_output>`,
82
+ ].join('\n'), session);
83
+ }
84
+ const formattedLines = result.matches.map((match) => formatLine(match.text, match.lineNumber, MAX_LINE_LENGTH));
85
+ const paginationMessage = `(${result.matches.length} of ${result.totalMatches} matches shown. Use offset=${offset + result.matches.length} to see more.)`;
86
+ const endMessage = `(${result.totalMatches} match${result.totalMatches === 1 ? '' : 'es'} from ${result.totalLines} total lines)`;
87
+ return appendSessionReminders(formatPtyOutput(id, session.status, pattern, formattedLines, result.hasMore, paginationMessage, endMessage), session);
88
+ }
89
+ /**
90
+ * Handles plain reading and formatting
91
+ */
92
+ function handlePlainRead(args, session, offset, limit) {
93
+ const result = manager.read(args.id, offset, limit);
94
+ if (!result) {
95
+ throw buildSessionNotFoundError(args.id);
96
+ }
97
+ if (result.lines.length === 0) {
98
+ return appendSessionReminders([
99
+ `<pty_output id="${args.id}" status="${session.status}">`,
100
+ `(No output available - buffer is empty)`,
101
+ `Total lines: ${result.totalLines}`,
102
+ `</pty_output>`,
103
+ ].join('\n'), session);
104
+ }
105
+ const formattedLines = result.lines.map((line, index) => formatLine(line, result.offset + index + 1, MAX_LINE_LENGTH));
106
+ const paginationMessage = `(Buffer has more lines. Use offset=${result.offset + result.lines.length} to read beyond line ${result.offset + result.lines.length})`;
107
+ const endMessage = `(End of buffer - total ${result.totalLines} lines)`;
108
+ return appendSessionReminders(formatPtyOutput(args.id, session.status, undefined, formattedLines, result.hasMore, paginationMessage, endMessage), session);
109
+ }
110
+ /**
111
+ * Formats a single line with line number and truncation
112
+ */
113
+ function validateRegex(pattern) {
114
+ try {
115
+ new RegExp(pattern);
116
+ // Check for potentially dangerous patterns that can cause exponential backtracking
117
+ // This is a basic check - more sophisticated validation could be added
118
+ const dangerousPatterns = [
119
+ /\(\?:.*\)\*.*\(\?:.*\)\*/, // nested optional groups with repetition
120
+ /.*\(\.\*\?\)\{2,\}.*/, // overlapping non-greedy quantifiers
121
+ /.*\(.*\|.*\)\{3,\}.*/, // complex alternation with repetition
122
+ ];
123
+ return !dangerousPatterns.some((dangerous) => dangerous.test(pattern));
124
+ }
125
+ catch {
126
+ return false;
127
+ }
128
+ }
129
+ export const ptyRead = tool({
130
+ description: DESCRIPTION,
131
+ args: {
132
+ id: tool.schema.string().describe('The PTY session ID (e.g., pty_a1b2c3d4)'),
133
+ offset: tool.schema
134
+ .number()
135
+ .optional()
136
+ .describe('Line number to start reading from (0-based, defaults to 0). When using pattern, this applies to filtered matches.'),
137
+ limit: tool.schema
138
+ .number()
139
+ .optional()
140
+ .describe('Number of lines to read (defaults to 500). When using pattern, this applies to filtered matches.'),
141
+ pattern: tool.schema
142
+ .string()
143
+ .optional()
144
+ .describe('Regex pattern to filter lines. When set, only matching lines are returned, then offset/limit apply to the matches.'),
145
+ ignoreCase: tool.schema
146
+ .boolean()
147
+ .optional()
148
+ .describe('Case-insensitive pattern matching (default: false)'),
149
+ },
150
+ async execute(args) {
151
+ const session = manager.get(args.id);
152
+ if (!session) {
153
+ throw buildSessionNotFoundError(args.id);
154
+ }
155
+ const offset = args.offset ?? 0;
156
+ const limit = args.limit ?? DEFAULT_READ_LIMIT;
157
+ if (args.pattern) {
158
+ return handlePatternRead(args.id, args.pattern, args.ignoreCase, session, offset, limit);
159
+ }
160
+ else {
161
+ return handlePlainRead(args, session, offset, limit);
162
+ }
163
+ },
164
+ });
165
+ //# sourceMappingURL=read.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"read.js","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/read.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ Reads output from a PTY session's buffer.
2
+
3
+ The PTY maintains a rolling buffer of output lines. Use offset and limit to paginate through the output, similar to reading a file.
4
+
5
+ Usage:
6
+ - `id`: The PTY session ID (from pty_spawn or pty_list)
7
+ - `offset`: Line number to start reading from (0-based, defaults to 0)
8
+ - `limit`: Number of lines to read (defaults to 500)
9
+ - `pattern`: Regex pattern to filter lines (optional)
10
+ - `ignoreCase`: Case-insensitive pattern matching (default: false)
11
+
12
+ Returns:
13
+ - Numbered lines of output (similar to cat -n format)
14
+ - Total line count in the buffer
15
+ - Indicator if more lines are available
16
+
17
+ The buffer stores up to PTY_MAX_BUFFER_LINES (default: 50000) lines. Older lines are discarded when the limit is reached.
18
+
19
+ Pattern Filtering:
20
+ - When `pattern` is set, lines are FILTERED FIRST using the regex, then offset/limit apply to the MATCHES
21
+ - Original line numbers are preserved so you can see where matches occurred in the buffer
22
+ - Supports full regex syntax (e.g., "error", "ERROR|WARN", "failed.*connection", etc.)
23
+ - If the pattern is invalid, an error message is returned explaining the issue
24
+ - If no lines match the pattern, a clear message indicates zero matches
25
+
26
+ Tips:
27
+ - To see the latest output, use a high offset or omit offset to read from the start
28
+ - To tail recent output, calculate offset as (totalLines - N) where N is how many recent lines you want
29
+ - Lines longer than 2000 characters are truncated
30
+ - Empty output may mean the process hasn't produced output yet
31
+ - If the session was started with `notifyOnExit=true`, do not use repeated `pty_read` calls only to detect completion; wait for the future `<pty_exited>` message instead
32
+
33
+ Examples:
34
+ - Read first 100 lines: offset=0, limit=100
35
+ - Read lines 500-600: offset=500, limit=100
36
+ - Read all available: omit both parameters
37
+ - Find errors: pattern="error", ignoreCase=true
38
+ - Find specific log levels: pattern="ERROR|WARN|FATAL"
39
+ - First 10 matches only: pattern="error", limit=10
@@ -0,0 +1,24 @@
1
+ export declare const ptySpawn: {
2
+ description: string;
3
+ args: {
4
+ command: import("zod").ZodString;
5
+ args: import("zod").ZodArray<import("zod").ZodString>;
6
+ workdir: import("zod").ZodOptional<import("zod").ZodString>;
7
+ env: import("zod").ZodOptional<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodString>>;
8
+ title: import("zod").ZodOptional<import("zod").ZodString>;
9
+ description: import("zod").ZodString;
10
+ notifyOnExit: import("zod").ZodOptional<import("zod").ZodBoolean>;
11
+ timeoutSeconds: import("zod").ZodNumber;
12
+ };
13
+ execute(args: {
14
+ command: string;
15
+ args: string[];
16
+ description: string;
17
+ timeoutSeconds: number;
18
+ workdir?: string | undefined;
19
+ env?: Record<string, string> | undefined;
20
+ title?: string | undefined;
21
+ notifyOnExit?: boolean | undefined;
22
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
23
+ };
24
+ //# sourceMappingURL=spawn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/spawn.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;CA+DnB,CAAA"}
@@ -0,0 +1,75 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import { manager } from "../manager.js";
3
+ import { checkWorkdirPermission } from "../permissions.js";
4
+ import DESCRIPTION from './spawn.txt';
5
+ const NOTIFY_ON_EXIT_INSTRUCTIONS = [
6
+ `<system_reminder>`,
7
+ `Completion signal for this session is the future \`<pty_exited>\` message.`,
8
+ `If you only need to know whether the command finished, do not call \`pty_read\`; wait for \`<pty_exited>\`.`,
9
+ `Never use sleep plus \`pty_read\` loops to check completion for this session.`,
10
+ `Call \`pty_read\` before exit only if you need live output now, the user explicitly asks for logs, or the exit notification reports a non-zero status and you need to investigate.`,
11
+ `</system_reminder>`,
12
+ ].join('\n');
13
+ function requireTimeoutSeconds(timeoutSeconds) {
14
+ if (!Number.isInteger(timeoutSeconds) || timeoutSeconds <= 0) {
15
+ throw new Error('timeoutSeconds must be a positive integer in seconds');
16
+ }
17
+ return timeoutSeconds;
18
+ }
19
+ export const ptySpawn = tool({
20
+ description: DESCRIPTION,
21
+ args: {
22
+ command: tool.schema.string().describe('The command/executable to run'),
23
+ args: tool.schema.array(tool.schema.string()).describe('Arguments to pass to the command'),
24
+ workdir: tool.schema.string().optional().describe('Working directory for the PTY session'),
25
+ env: tool.schema
26
+ .record(tool.schema.string(), tool.schema.string())
27
+ .optional()
28
+ .describe('Additional environment variables'),
29
+ title: tool.schema.string().optional().describe('Human-readable title for the session'),
30
+ description: tool.schema
31
+ .string()
32
+ .describe('Clear, concise description of what this PTY session is for in 5-10 words'),
33
+ notifyOnExit: tool.schema
34
+ .boolean()
35
+ .optional()
36
+ .describe('If true, sends a notification to the session when the process exits (default: false)'),
37
+ timeoutSeconds: tool.schema
38
+ .number()
39
+ .describe('Required positive integer timeout in seconds. The PTY is killed automatically when this duration elapses.'),
40
+ },
41
+ async execute(args, ctx) {
42
+ const timeoutSeconds = requireTimeoutSeconds(args.timeoutSeconds);
43
+ if (args.workdir) {
44
+ await checkWorkdirPermission(args.workdir);
45
+ }
46
+ const sessionId = ctx.sessionID;
47
+ const info = manager.spawn({
48
+ command: args.command,
49
+ args: args.args,
50
+ workdir: args.workdir,
51
+ env: args.env,
52
+ title: args.title,
53
+ description: args.description,
54
+ parentSessionId: sessionId,
55
+ parentAgent: ctx.agent,
56
+ notifyOnExit: args.notifyOnExit,
57
+ timeoutSeconds,
58
+ });
59
+ const output = [
60
+ `<pty_spawned>`,
61
+ `ID: ${info.id}`,
62
+ `Title: ${info.title}`,
63
+ `Command: ${info.command} ${info.args.join(' ')}`,
64
+ `Workdir: ${info.workdir}`,
65
+ `PID: ${info.pid}`,
66
+ `Status: ${info.status}`,
67
+ `NotifyOnExit: ${info.notifyOnExit}`,
68
+ `TimeoutSeconds: ${info.timeoutSeconds}`,
69
+ `</pty_spawned>`,
70
+ ...(info.notifyOnExit ? ['', NOTIFY_ON_EXIT_INSTRUCTIONS] : []),
71
+ ].join('\n');
72
+ return output;
73
+ },
74
+ });
75
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/spawn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,52 @@
1
+ Spawns a new interactive PTY (pseudo-terminal) session that runs in the background.
2
+
3
+ Unlike the built-in bash tool which runs commands synchronously and waits for completion, PTY sessions persist and allow you to:
4
+ - Run long-running processes (dev servers, watch modes, etc.)
5
+ - Send interactive input (including Ctrl+C, arrow keys, etc.)
6
+ - Read output at any time
7
+ - Manage multiple concurrent terminal sessions
8
+
9
+ Usage:
10
+ - The `command` parameter is required (e.g., "npm", "python", "bash")
11
+ - Use `args` to pass arguments to the command (e.g., ["run", "dev"])
12
+ - Use `workdir` to set the working directory (defaults to project root)
13
+ - Use `env` to set additional environment variables
14
+ - Use `title` to give the session a human-readable name
15
+ - The `description` parameter is required: a clear, concise 5-10 word description
16
+ - Use `notifyOnExit` to receive a notification when the process exits (default: false)
17
+ - Set `timeoutSeconds` on every spawn. It is required and must be a positive integer.
18
+ - Use a long but finite timeout for sessions that are meant to keep running, such as dev servers, watch modes, local APIs, or REPLs.
19
+ - Prefer a timeout that matches the expected wall-clock budget for builds, unit test suites, end-to-end tests, migrations, or other commands where you are waiting for a result.
20
+
21
+ Returns the session info including:
22
+ - `id`: Unique identifier (pty_XXXXXXXX) for use with other pty_* tools
23
+ - `pid`: Process ID
24
+ - `status`: Current status ("running")
25
+
26
+ After spawning, use:
27
+ - `pty_write` to send input to the PTY
28
+ - `pty_read` to read output from the PTY
29
+ - `pty_list` to see all active PTY sessions
30
+ - `pty_kill` to terminate the PTY
31
+
32
+ Exit Notifications:
33
+ When `notifyOnExit` is true, you will receive a message when the process exits containing:
34
+ - Session ID and title
35
+ - Exit code
36
+ - Total output lines
37
+ - Last line of output (truncated to 250 chars)
38
+
39
+ This is useful for long-running processes where you want to be notified when they complete
40
+ instead of polling with `pty_read`.
41
+ - Completion signal is the future `<pty_exited>` message
42
+ - If you only need to know whether the command finished, do not call `pty_read`; wait for `<pty_exited>`
43
+ - Never use sleep plus `pty_read` loops to check completion
44
+ - Use `pty_read` before exit only if you need live output now, the user explicitly asks for logs, or the exit notification reports a non-zero status and you need to investigate
45
+
46
+ Examples:
47
+ - Start a dev server: command="npm", args=["run", "dev"], title="Dev Server"
48
+ - Start a timed dev server when explicitly requested: command="npm", args=["run", "dev"], title="Dev Server", timeoutSeconds=600
49
+ - Start a Python REPL: command="python3", title="Python REPL"
50
+ - Run tests in watch mode: command="npm", args=["test", "--", "--watch"]
51
+ - Run build with notification and timeout: command="npm", args=["run", "build"], title="Build", notifyOnExit=true, timeoutSeconds=900
52
+ - Run end-to-end tests with timeout: command="npm", args=["run", "test:e2e"], title="E2E Tests", notifyOnExit=true, timeoutSeconds=1800
@@ -0,0 +1,12 @@
1
+ export declare const ptyWrite: {
2
+ description: string;
3
+ args: {
4
+ id: import("zod").ZodString;
5
+ data: import("zod").ZodString;
6
+ };
7
+ execute(args: {
8
+ id: string;
9
+ data: string;
10
+ }, context: import("@opencode-ai/plugin").ToolContext): Promise<string>;
11
+ };
12
+ //# sourceMappingURL=write.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.d.ts","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/write.ts"],"names":[],"mappings":"AAmCA,eAAO,MAAM,QAAQ;;;;;;;;;;CAgCnB,CAAA"}
@@ -0,0 +1,62 @@
1
+ import { tool } from '@opencode-ai/plugin';
2
+ import { manager } from "../manager.js";
3
+ import { buildSessionNotFoundError } from "../utils.js";
4
+ import DESCRIPTION from './write.txt';
5
+ const ETX = String.fromCharCode(3);
6
+ const EOT = String.fromCharCode(4);
7
+ /**
8
+ * Parse escape sequences in a string to their actual byte values.
9
+ * Handles: \n, \r, \t, \xNN (hex), \uNNNN (unicode), \\
10
+ */
11
+ function parseEscapeSequences(input) {
12
+ return input.replace(/\\(x[0-9A-Fa-f]{2}|u[0-9A-Fa-f]{4}|[nrt\\])/g, (match, seq) => {
13
+ if (seq.startsWith('x')) {
14
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
15
+ }
16
+ if (seq.startsWith('u')) {
17
+ return String.fromCharCode(parseInt(seq.slice(1), 16));
18
+ }
19
+ switch (seq) {
20
+ case 'n':
21
+ return '\n';
22
+ case 'r':
23
+ return '\r';
24
+ case 't':
25
+ return '\t';
26
+ case '\\':
27
+ return '\\';
28
+ default:
29
+ return match;
30
+ }
31
+ });
32
+ }
33
+ export const ptyWrite = tool({
34
+ description: DESCRIPTION,
35
+ args: {
36
+ id: tool.schema.string().describe('The PTY session ID (e.g., pty_a1b2c3d4)'),
37
+ data: tool.schema.string().describe('The input data to send to the PTY'),
38
+ },
39
+ async execute(args) {
40
+ const session = manager.get(args.id);
41
+ if (!session) {
42
+ throw buildSessionNotFoundError(args.id);
43
+ }
44
+ if (session.status !== 'running') {
45
+ throw new Error(`Cannot write to PTY '${args.id}' - session status is '${session.status}'.`);
46
+ }
47
+ // Parse escape sequences to actual bytes
48
+ const parsedData = parseEscapeSequences(args.data);
49
+ const success = manager.write(args.id, parsedData);
50
+ if (!success) {
51
+ throw new Error(`Failed to write to PTY '${args.id}'.`);
52
+ }
53
+ const preview = args.data.length > 50 ? `${args.data.slice(0, 50)}...` : args.data;
54
+ const displayPreview = preview
55
+ .replace(new RegExp(ETX, 'g'), '^C')
56
+ .replace(new RegExp(EOT, 'g'), '^D')
57
+ .replace(/\n/g, '\\n')
58
+ .replace(/\r/g, '\\r');
59
+ return `Sent ${args.data.length} bytes to ${args.id}: "${displayPreview}"`;
60
+ },
61
+ });
62
+ //# sourceMappingURL=write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write.js","sourceRoot":"","sources":["../../../../../src/plugin/pty/tools/write.ts"],"names":[],"mappings":"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"}