@playdrop/playdrop-cli 0.15.29 → 0.15.30

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 (49) hide show
  1. package/config/client-meta.json +4 -4
  2. package/dist/commands/chat.d.ts +27 -0
  3. package/dist/commands/chat.js +224 -0
  4. package/dist/commands/upgrade.js +1 -1
  5. package/dist/commands/worker/provider-telemetry.d.ts +4 -0
  6. package/dist/commands/worker/provider-telemetry.js +30 -3
  7. package/dist/commands/worker.d.ts +0 -8
  8. package/dist/commands/worker.js +0 -80
  9. package/dist/index.js +43 -0
  10. package/node_modules/@playdrop/api-client/dist/client.d.ts +25 -1
  11. package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
  12. package/node_modules/@playdrop/api-client/dist/client.js +14 -0
  13. package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +60 -0
  14. package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -0
  15. package/node_modules/@playdrop/api-client/dist/domains/chat.js +208 -0
  16. package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts +2 -1
  17. package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts.map +1 -1
  18. package/node_modules/@playdrop/api-client/dist/domains/free-credits.js +11 -0
  19. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -0
  20. package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
  21. package/node_modules/@playdrop/api-client/dist/domains/payments.js +10 -0
  22. package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts +26 -0
  23. package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts.map +1 -0
  24. package/node_modules/@playdrop/api-client/dist/domains/playtrade.js +91 -0
  25. package/node_modules/@playdrop/api-client/dist/index.d.ts +9 -1
  26. package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
  27. package/node_modules/@playdrop/api-client/dist/index.js +35 -0
  28. package/node_modules/@playdrop/config/client-meta.json +4 -4
  29. package/node_modules/@playdrop/types/dist/api.d.ts +41 -5
  30. package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
  31. package/node_modules/@playdrop/types/dist/api.js +33 -0
  32. package/node_modules/@playdrop/types/dist/app-review.d.ts +1 -1
  33. package/node_modules/@playdrop/types/dist/app-review.d.ts.map +1 -1
  34. package/node_modules/@playdrop/types/dist/app-review.js +1 -0
  35. package/node_modules/@playdrop/types/dist/attachment-policy.d.ts +8 -0
  36. package/node_modules/@playdrop/types/dist/attachment-policy.d.ts.map +1 -0
  37. package/node_modules/@playdrop/types/dist/attachment-policy.js +58 -0
  38. package/node_modules/@playdrop/types/dist/chat.d.ts +104 -0
  39. package/node_modules/@playdrop/types/dist/chat.d.ts.map +1 -0
  40. package/node_modules/@playdrop/types/dist/chat.js +40 -0
  41. package/node_modules/@playdrop/types/dist/index.d.ts +3 -0
  42. package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
  43. package/node_modules/@playdrop/types/dist/index.js +7 -1
  44. package/node_modules/@playdrop/types/dist/playtrade.d.ts +72 -0
  45. package/node_modules/@playdrop/types/dist/playtrade.d.ts.map +1 -0
  46. package/node_modules/@playdrop/types/dist/playtrade.js +27 -0
  47. package/node_modules/@playdrop/types/dist/version.d.ts +6 -0
  48. package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
  49. package/package.json +1 -1
@@ -1,8 +1,8 @@
1
1
  {
2
- "version": "0.15.29",
3
- "build": 12,
4
- "runtimeSdkVersion": "0.15.29",
5
- "runtimeSdkBuild": 12,
2
+ "version": "0.15.30",
3
+ "build": 13,
4
+ "runtimeSdkVersion": "0.15.30",
5
+ "runtimeSdkBuild": 13,
6
6
  "clients": {
7
7
  "all": {
8
8
  "minimumVersion": "0.7.4"
@@ -0,0 +1,27 @@
1
+ export type ChatCommonOptions = {
2
+ env?: string;
3
+ json?: boolean;
4
+ };
5
+ export type ChatWriteCommandOptions = ChatCommonOptions & {
6
+ text?: string;
7
+ textFile?: string;
8
+ file?: string[];
9
+ link?: string[];
10
+ clientKey?: string;
11
+ eventType?: string;
12
+ eventDataFile?: string;
13
+ asSystem?: boolean;
14
+ };
15
+ export declare function listChatChannels(options?: ChatCommonOptions): Promise<void>;
16
+ export declare function readChatChannel(channel: string, options?: ChatCommonOptions): Promise<void>;
17
+ export declare function readChatThread(rawMessageId: string, options?: ChatCommonOptions): Promise<void>;
18
+ export declare function sendChatMessage(channel: string, options: ChatWriteCommandOptions): Promise<void>;
19
+ export declare function replyChatMessage(rawMessageId: string, options: ChatWriteCommandOptions): Promise<void>;
20
+ export declare function editChatMessage(rawMessageId: string, options: ChatWriteCommandOptions): Promise<void>;
21
+ export declare function reactChatMessage(rawMessageId: string, emoji: string, options?: ChatCommonOptions): Promise<void>;
22
+ export declare function unreactChatMessage(rawMessageId: string, emoji: string, options?: ChatCommonOptions): Promise<void>;
23
+ export declare function grantChatAccess(channel: string, username: string, options?: ChatCommonOptions & {
24
+ readOnly?: boolean;
25
+ }): Promise<void>;
26
+ export declare function revokeChatAccess(channel: string, username: string, options?: ChatCommonOptions): Promise<void>;
27
+ export declare function setChatVisibility(channel: string, visibility: string, options?: ChatCommonOptions): Promise<void>;
@@ -0,0 +1,224 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listChatChannels = listChatChannels;
7
+ exports.readChatChannel = readChatChannel;
8
+ exports.readChatThread = readChatThread;
9
+ exports.sendChatMessage = sendChatMessage;
10
+ exports.replyChatMessage = replyChatMessage;
11
+ exports.editChatMessage = editChatMessage;
12
+ exports.reactChatMessage = reactChatMessage;
13
+ exports.unreactChatMessage = unreactChatMessage;
14
+ exports.grantChatAccess = grantChatAccess;
15
+ exports.revokeChatAccess = revokeChatAccess;
16
+ exports.setChatVisibility = setChatVisibility;
17
+ const node_fs_1 = require("node:fs");
18
+ const node_path_1 = __importDefault(require("node:path"));
19
+ const commandContext_1 = require("../commandContext");
20
+ const errors_1 = require("../errors");
21
+ const messages_1 = require("../messages");
22
+ const output_1 = require("../output");
23
+ function positiveId(raw, command) {
24
+ const value = Number(raw);
25
+ if (!Number.isInteger(value) || value <= 0) {
26
+ (0, messages_1.printErrorWithHelp)('Message id must be a positive integer.', [], { command });
27
+ process.exitCode = 1;
28
+ return null;
29
+ }
30
+ return value;
31
+ }
32
+ function contentTypeForFile(filePath) {
33
+ const extension = node_path_1.default.extname(filePath).toLowerCase();
34
+ if (extension === '.gif')
35
+ return 'image/gif';
36
+ if (extension === '.jpg' || extension === '.jpeg')
37
+ return 'image/jpeg';
38
+ if (extension === '.png')
39
+ return 'image/png';
40
+ if (extension === '.webp')
41
+ return 'image/webp';
42
+ if (extension === '.mp4')
43
+ return 'video/mp4';
44
+ if (extension === '.mov')
45
+ return 'video/quicktime';
46
+ if (extension === '.webm')
47
+ return 'video/webm';
48
+ if (extension === '.txt')
49
+ return 'text/plain';
50
+ if (extension === '.md' || extension === '.markdown')
51
+ return 'text/markdown';
52
+ if (extension === '.json')
53
+ return 'application/json';
54
+ throw new Error(`unsupported_chat_file:${filePath}`);
55
+ }
56
+ async function readWriteOptions(options) {
57
+ if (options.text !== undefined && options.textFile !== undefined) {
58
+ throw new Error('chat_text_and_text_file_conflict');
59
+ }
60
+ const body = options.textFile !== undefined
61
+ ? await node_fs_1.promises.readFile(node_path_1.default.resolve(options.textFile), 'utf8')
62
+ : options.text;
63
+ const eventData = options.eventDataFile !== undefined
64
+ ? JSON.parse(await node_fs_1.promises.readFile(node_path_1.default.resolve(options.eventDataFile), 'utf8'))
65
+ : undefined;
66
+ const files = await Promise.all((options.file ?? []).map(async (filePath) => {
67
+ const absolutePath = node_path_1.default.resolve(filePath);
68
+ const buffer = await node_fs_1.promises.readFile(absolutePath);
69
+ return {
70
+ file: new Blob([new Uint8Array(buffer)], { type: contentTypeForFile(absolutePath) }),
71
+ fileName: node_path_1.default.basename(absolutePath),
72
+ };
73
+ }));
74
+ return {
75
+ ...(body !== undefined ? { body } : {}),
76
+ ...(files.length > 0 ? { files } : {}),
77
+ ...(options.link?.length ? { links: options.link } : {}),
78
+ ...(options.clientKey !== undefined ? { clientKey: options.clientKey } : {}),
79
+ ...(options.eventType !== undefined ? { eventType: options.eventType } : {}),
80
+ ...(eventData !== undefined ? { eventData } : {}),
81
+ ...(options.asSystem !== undefined ? { asSystem: options.asSystem } : {}),
82
+ };
83
+ }
84
+ function printMessage(message, indent = '') {
85
+ const edited = message.editedAt ? ' edited' : '';
86
+ console.log(`${indent}#${message.id} ${message.author.displayName} [${message.author.kind}] | ${(0, output_1.formatTimestamp)(message.createdAt)}${edited}`);
87
+ if (message.body)
88
+ console.log(`${indent}${message.body.replace(/\n/g, `\n${indent}`)}`);
89
+ for (const attachment of message.attachments) {
90
+ console.log(`${indent}[${attachment.kind}] ${attachment.fileName ?? attachment.url} ${attachment.url}`);
91
+ }
92
+ if (message.reactions.length > 0) {
93
+ console.log(`${indent}${message.reactions.map((reaction) => `${reaction.emoji} ${reaction.count}`).join(' ')}`);
94
+ }
95
+ if (message.replyCount > 0)
96
+ console.log(`${indent}${message.replyCount} ${message.replyCount === 1 ? 'reply' : 'replies'}`);
97
+ }
98
+ async function runChatCommand(command, options, operation) {
99
+ await (0, commandContext_1.withEnvironment)(command, 'PlayDrop Chat', async ({ client }) => {
100
+ try {
101
+ await operation(client);
102
+ }
103
+ catch (error) {
104
+ if (!(0, errors_1.handleCommandFailure)(error, command, 'PlayDrop Chat'))
105
+ throw error;
106
+ }
107
+ }, { env: options.env });
108
+ }
109
+ async function listChatChannels(options = {}) {
110
+ await runChatCommand('chat channels', options, async (client) => {
111
+ const response = await client.fetchChatChannels();
112
+ if (options.json)
113
+ return (0, output_1.printJson)(response);
114
+ for (const channel of response.channels) {
115
+ console.log(`${channel.slug}\t${channel.displayName}\t${channel.readPolicy}\t${channel.canWrite ? 'write' : 'read'}`);
116
+ }
117
+ });
118
+ }
119
+ async function readChatChannel(channel, options = {}) {
120
+ await runChatCommand('chat read', options, async (client) => {
121
+ const response = await client.fetchChatMessages(channel);
122
+ if (options.json)
123
+ return (0, output_1.printJson)(response);
124
+ if (response.messages.length === 0)
125
+ return console.log(`No messages in ${channel}.`);
126
+ response.messages.forEach((message) => printMessage(message));
127
+ });
128
+ }
129
+ async function readChatThread(rawMessageId, options = {}) {
130
+ const messageId = positiveId(rawMessageId, 'chat thread');
131
+ if (!messageId)
132
+ return;
133
+ await runChatCommand('chat thread', options, async (client) => {
134
+ const response = await client.fetchChatThread(messageId);
135
+ if (options.json)
136
+ return (0, output_1.printJson)(response);
137
+ if (response.messages.length === 0)
138
+ return console.log(`No replies to message #${messageId}.`);
139
+ response.messages.forEach((message) => printMessage(message, ' '));
140
+ });
141
+ }
142
+ async function sendChatMessage(channel, options) {
143
+ await runChatCommand('chat send', options, async (client) => {
144
+ const response = await client.sendChatMessage(channel, await readWriteOptions(options));
145
+ if (options.json)
146
+ return (0, output_1.printJson)(response);
147
+ (0, output_1.printSuccess)(`Message #${response.message.id} sent to ${channel}.`);
148
+ });
149
+ }
150
+ async function replyChatMessage(rawMessageId, options) {
151
+ const messageId = positiveId(rawMessageId, 'chat reply');
152
+ if (!messageId)
153
+ return;
154
+ await runChatCommand('chat reply', options, async (client) => {
155
+ const response = await client.replyChatMessage(messageId, await readWriteOptions(options));
156
+ if (options.json)
157
+ return (0, output_1.printJson)(response);
158
+ (0, output_1.printSuccess)(`Reply #${response.message.id} sent.`);
159
+ });
160
+ }
161
+ async function editChatMessage(rawMessageId, options) {
162
+ const messageId = positiveId(rawMessageId, 'chat edit');
163
+ if (!messageId)
164
+ return;
165
+ await runChatCommand('chat edit', options, async (client) => {
166
+ const write = await readWriteOptions(options);
167
+ const response = await client.editChatMessage(messageId, write);
168
+ if (options.json)
169
+ return (0, output_1.printJson)(response);
170
+ (0, output_1.printSuccess)(`Message #${messageId} edited.`);
171
+ });
172
+ }
173
+ async function reactChatMessage(rawMessageId, emoji, options = {}) {
174
+ const messageId = positiveId(rawMessageId, 'chat react');
175
+ if (!messageId)
176
+ return;
177
+ await runChatCommand('chat react', options, async (client) => {
178
+ const response = await client.reactChatMessage(messageId, emoji);
179
+ if (options.json)
180
+ return (0, output_1.printJson)(response);
181
+ (0, output_1.printSuccess)(`${emoji} added to message #${messageId}.`);
182
+ });
183
+ }
184
+ async function unreactChatMessage(rawMessageId, emoji, options = {}) {
185
+ const messageId = positiveId(rawMessageId, 'chat unreact');
186
+ if (!messageId)
187
+ return;
188
+ await runChatCommand('chat unreact', options, async (client) => {
189
+ const response = await client.unreactChatMessage(messageId, emoji);
190
+ if (options.json)
191
+ return (0, output_1.printJson)(response);
192
+ (0, output_1.printSuccess)(`${emoji} removed from message #${messageId}.`);
193
+ });
194
+ }
195
+ async function grantChatAccess(channel, username, options = {}) {
196
+ await runChatCommand('chat access grant', options, async (client) => {
197
+ const response = await client.grantChatAccess(channel, username, !options.readOnly);
198
+ if (options.json)
199
+ return (0, output_1.printJson)(response);
200
+ (0, output_1.printSuccess)(`${username} now has ${response.canWrite ? 'write' : 'read'} access to ${channel}.`);
201
+ });
202
+ }
203
+ async function revokeChatAccess(channel, username, options = {}) {
204
+ await runChatCommand('chat access revoke', options, async (client) => {
205
+ await client.revokeChatAccess(channel, username);
206
+ if (options.json)
207
+ return (0, output_1.printJson)({ success: true, channel, username });
208
+ (0, output_1.printSuccess)(`${username} no longer has access to ${channel}.`);
209
+ });
210
+ }
211
+ async function setChatVisibility(channel, visibility, options = {}) {
212
+ const readPolicy = visibility === 'public' ? 'PUBLIC' : visibility === 'admin-only' ? 'ADMIN_ONLY' : null;
213
+ if (!readPolicy) {
214
+ (0, messages_1.printErrorWithHelp)('Visibility must be public or admin-only.', [], { command: 'chat visibility' });
215
+ process.exitCode = 1;
216
+ return;
217
+ }
218
+ await runChatCommand('chat visibility', options, async (client) => {
219
+ const response = await client.setChatVisibility(channel, readPolicy);
220
+ if (options.json)
221
+ return (0, output_1.printJson)(response);
222
+ (0, output_1.printSuccess)(`${channel} visibility is now ${visibility}.`);
223
+ });
224
+ }
@@ -84,7 +84,7 @@ async function observeManagedWorkerUpdate(input) {
84
84
  }
85
85
  await sleep(pollMs);
86
86
  }
87
- throw new Error(`playdrop_update_worker_timeout: worker ${input.workerKey} did not complete update intent ${input.intentId} within ${timeoutMs}ms. Next action: check Slack worker alerts, launchctl status, and the wrapper failure marker on the worker host.`);
87
+ throw new Error(`playdrop_update_worker_timeout: worker ${input.workerKey} did not complete update intent ${input.intentId} within ${timeoutMs}ms. Next action: check PlayDrop Chat, launchctl status, and the wrapper failure marker on the worker host.`);
88
88
  }
89
89
  async function upgrade(options = {}) {
90
90
  if (options.worker) {
@@ -6,6 +6,10 @@ export interface WorkerAgentProviderTelemetry {
6
6
  weeklyUsageStatus: AgentWorkerWeeklyUsageStatus;
7
7
  telemetryCheckedAt: string;
8
8
  }
9
+ export declare function calculateWeeklyUsageRouting(remainingPercent: number, resetsAt: string | null, checkedAt: string): {
10
+ targetRemainingPercent: number;
11
+ surplusPercent: number;
12
+ } | null;
9
13
  export declare function buildCodexProviderTelemetry(accountResult: unknown, rateLimitsResult: unknown, checkedAt: string): WorkerAgentProviderTelemetry;
10
14
  export declare function buildClaudeProviderTelemetry(authStatus: unknown, credentialMetadata: unknown, usageResult: unknown, checkedAt: string): WorkerAgentProviderTelemetry;
11
15
  export declare function probeWorkerProviderTelemetry(capabilities: AgentWorkerCapabilities): Promise<Partial<Record<AgentRuntime, WorkerAgentProviderTelemetry>>>;
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.calculateWeeklyUsageRouting = calculateWeeklyUsageRouting;
6
7
  exports.buildCodexProviderTelemetry = buildCodexProviderTelemetry;
7
8
  exports.buildClaudeProviderTelemetry = buildClaudeProviderTelemetry;
8
9
  exports.probeWorkerProviderTelemetry = probeWorkerProviderTelemetry;
@@ -15,6 +16,8 @@ const clientInfo_1 = require("../../clientInfo");
15
16
  const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
16
17
  const PROVIDER_TELEMETRY_TIMEOUT_MS = 10000;
17
18
  const WEEKLY_WINDOW_MINUTES = 7 * 24 * 60;
19
+ const WEEKLY_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
20
+ const WEEKLY_USAGE_RESERVE_PERCENT = 10;
18
21
  function asObject(value) {
19
22
  return typeof value === 'object' && value !== null && !Array.isArray(value)
20
23
  ? value
@@ -29,6 +32,25 @@ function normalizePercent(value, field) {
29
32
  }
30
33
  return Math.round(value * 10) / 10;
31
34
  }
35
+ function roundPercent(value) {
36
+ return Math.round(value * 10) / 10;
37
+ }
38
+ function calculateWeeklyUsageRouting(remainingPercent, resetsAt, checkedAt) {
39
+ if (!resetsAt)
40
+ return null;
41
+ const resetMs = new Date(resetsAt).getTime();
42
+ const checkedMs = new Date(checkedAt).getTime();
43
+ if (!Number.isFinite(resetMs) || !Number.isFinite(checkedMs)) {
44
+ throw new Error('worker_weekly_usage_routing_time_invalid');
45
+ }
46
+ const remainingWindowRatio = Math.max(0, Math.min(1, (resetMs - checkedMs) / WEEKLY_WINDOW_MS));
47
+ const targetRemainingPercent = roundPercent(WEEKLY_USAGE_RESERVE_PERCENT
48
+ + (100 - WEEKLY_USAGE_RESERVE_PERCENT) * remainingWindowRatio);
49
+ return {
50
+ targetRemainingPercent,
51
+ surplusPercent: roundPercent(remainingPercent - targetRemainingPercent),
52
+ };
53
+ }
32
54
  function unavailableTelemetry(checkedAt) {
33
55
  return {
34
56
  account: null,
@@ -87,17 +109,20 @@ function buildCodexProviderTelemetry(accountResult, rateLimitsResult, checkedAt)
87
109
  };
88
110
  }
89
111
  const usedPercent = normalizePercent(weeklyWindow.usedPercent, 'codex_weekly_usage');
112
+ const remainingPercent = roundPercent(100 - usedPercent);
90
113
  const resetsAtSeconds = weeklyWindow.resetsAt;
91
114
  if (typeof resetsAtSeconds !== 'number' || !Number.isFinite(resetsAtSeconds) || resetsAtSeconds <= 0) {
92
115
  throw new Error('codex_weekly_reset_invalid');
93
116
  }
117
+ const resetsAt = new Date(resetsAtSeconds * 1000).toISOString();
94
118
  return {
95
119
  account: accountLabel,
96
120
  plan,
97
121
  weeklyUsage: {
98
122
  usedPercent,
99
- remainingPercent: Math.round((100 - usedPercent) * 10) / 10,
100
- resetsAt: new Date(resetsAtSeconds * 1000).toISOString(),
123
+ remainingPercent,
124
+ resetsAt,
125
+ ...calculateWeeklyUsageRouting(remainingPercent, resetsAt, checkedAt),
101
126
  },
102
127
  weeklyUsageStatus: 'AVAILABLE',
103
128
  telemetryCheckedAt: checkedAt,
@@ -138,6 +163,7 @@ function buildClaudeProviderTelemetry(authStatus, credentialMetadata, usageResul
138
163
  };
139
164
  }
140
165
  const usedPercent = normalizePercent(sevenDay.utilization, 'claude_weekly_usage');
166
+ const remainingPercent = roundPercent(100 - usedPercent);
141
167
  const rawReset = sevenDay.resets_at;
142
168
  const resetsAt = rawReset === null
143
169
  ? null
@@ -153,8 +179,9 @@ function buildClaudeProviderTelemetry(authStatus, credentialMetadata, usageResul
153
179
  plan,
154
180
  weeklyUsage: {
155
181
  usedPercent,
156
- remainingPercent: Math.round((100 - usedPercent) * 10) / 10,
182
+ remainingPercent,
157
183
  resetsAt: resetsAt ? new Date(resetsAt).toISOString() : null,
184
+ ...calculateWeeklyUsageRouting(remainingPercent, resetsAt ? new Date(resetsAt).toISOString() : null, checkedAt),
158
185
  },
159
186
  weeklyUsageStatus: 'AVAILABLE',
160
187
  telemetryCheckedAt: checkedAt,
@@ -139,13 +139,6 @@ type TaskSubmitEvalOptions = {
139
139
  evidenceDir?: string[];
140
140
  resultFile?: string;
141
141
  };
142
- export type WorkerHealthAlertInput = {
143
- state: 'started' | 'stopped' | 'crashed';
144
- env: string;
145
- workerName: string;
146
- taskId?: number | null;
147
- detail?: string | null;
148
- };
149
142
  type WorkerTaskState = {
150
143
  taskId: number;
151
144
  env: string;
@@ -258,7 +251,6 @@ export declare function resolveDevPluginWorkingTree(input: {
258
251
  }): string | null;
259
252
  export declare function discoverWorkerProjectRoot(workspaceDir: string): string;
260
253
  export declare function readWorkerTaskState(): WorkerTaskState | null;
261
- export declare function buildWorkerHealthAlertText(input: WorkerHealthAlertInput): string;
262
254
  export declare function isWorkerAuthFailureError(error: unknown): boolean;
263
255
  export declare function classifyWorkerEventDrainError(error: unknown): 'session_expired' | 'lease_invalid' | null;
264
256
  export declare function isWorkerTransientNetworkError(error: unknown): boolean;
@@ -32,7 +32,6 @@ exports.stageAssignmentPluginBundle = stageAssignmentPluginBundle;
32
32
  exports.resolveDevPluginWorkingTree = resolveDevPluginWorkingTree;
33
33
  exports.discoverWorkerProjectRoot = discoverWorkerProjectRoot;
34
34
  exports.readWorkerTaskState = readWorkerTaskState;
35
- exports.buildWorkerHealthAlertText = buildWorkerHealthAlertText;
36
35
  exports.isWorkerAuthFailureError = isWorkerAuthFailureError;
37
36
  exports.classifyWorkerEventDrainError = classifyWorkerEventDrainError;
38
37
  exports.isWorkerTransientNetworkError = isWorkerTransientNetworkError;
@@ -153,7 +152,6 @@ const CLAIM_BACKOFF_MAX_MS = 60000;
153
152
  const CLAIM_BACKOFF_JITTER_MS = 500;
154
153
  const FAIL_REPORT_RETRY_DELAY_MS = 2000;
155
154
  const WORKER_TRANSCRIPT_CHUNK_BATCH_SIZE = 100;
156
- const SLACK_API_BASE_URL = 'https://slack.com/api';
157
155
  const WORKER_WRAPPER_CONTRACT_VERSION = 3;
158
156
  const WORKER_SUPPORTED_KINDS = ['NEW_GAME', 'STATIC_GAME', 'REMIX_GAME', 'GAME_UPDATE', 'GAME_REVIEW', 'GAME_EVAL', 'RELEASE_SMOKE'];
159
157
  const GAME_CREATION_TASK_KINDS = new Set(['NEW_GAME', 'STATIC_GAME', 'REMIX_GAME', 'GAME_UPDATE']);
@@ -1472,59 +1470,6 @@ function readWorkerTaskState() {
1472
1470
  }
1473
1471
  return parsed;
1474
1472
  }
1475
- function isWorkerSlackAlertEnabled() {
1476
- const normalized = node_process_1.default.env.PLAYDROP_WORKER_SLACK_ALERTS?.trim().toLowerCase();
1477
- return normalized === '1' || normalized === 'true' || normalized === 'yes' || normalized === 'on';
1478
- }
1479
- function buildWorkerHealthAlertText(input) {
1480
- return [
1481
- `PlayDrop Worker ${input.state}`,
1482
- `env=${input.env}`,
1483
- `worker=${input.workerName}`,
1484
- input.taskId ? `task=${input.taskId}` : null,
1485
- input.detail ? `detail=${input.detail}` : null,
1486
- ].filter(Boolean).join(' ');
1487
- }
1488
- async function sendWorkerHealthAlert(input) {
1489
- if (!isWorkerSlackAlertEnabled()) {
1490
- return;
1491
- }
1492
- const token = node_process_1.default.env.PLAYDROP_SLACK_BOT_TOKEN?.trim();
1493
- if (!token) {
1494
- throw new Error('missing_playdrop_slack_bot_token');
1495
- }
1496
- const channelId = node_process_1.default.env.PLAYDROP_WORKER_SLACK_CHANNEL_ID?.trim();
1497
- if (!channelId) {
1498
- throw new Error('missing_playdrop_worker_slack_channel_id');
1499
- }
1500
- const response = await fetch(`${SLACK_API_BASE_URL}/chat.postMessage`, {
1501
- method: 'POST',
1502
- headers: {
1503
- authorization: `Bearer ${token}`,
1504
- 'content-type': 'application/json; charset=utf-8',
1505
- },
1506
- body: JSON.stringify({
1507
- channel: channelId,
1508
- text: buildWorkerHealthAlertText(input),
1509
- }),
1510
- });
1511
- const raw = await response.text();
1512
- const payload = raw ? JSON.parse(raw) : {};
1513
- if (!response.ok) {
1514
- throw new Error(`slack_http_error:${response.status}`);
1515
- }
1516
- if (payload.ok !== true) {
1517
- throw new Error(`slack_api_error:${String(payload.error || 'unknown')}`);
1518
- }
1519
- }
1520
- async function sendWorkerHealthAlertSafely(input) {
1521
- try {
1522
- await sendWorkerHealthAlert(input);
1523
- }
1524
- catch (error) {
1525
- console.error(`Worker health alert failed: ${error instanceof Error ? error.message : String(error)}`);
1526
- }
1527
- }
1528
1473
  function parsePositiveInteger(value, code) {
1529
1474
  const parsed = typeof value === 'number' ? value : Number.parseInt(String(value ?? ''), 10);
1530
1475
  if (!Number.isInteger(parsed) || parsed <= 0) {
@@ -4229,7 +4174,6 @@ async function startWorker(options = {}) {
4229
4174
  let shuttingDown = false;
4230
4175
  let quiescingForUpdate = false;
4231
4176
  let shutdownSignal = null;
4232
- let crashed = false;
4233
4177
  let fatalTaskError = null;
4234
4178
  let presenceTimer = null;
4235
4179
  let currentWorkerPolicy = initialWorkerPolicy;
@@ -4328,7 +4272,6 @@ async function startWorker(options = {}) {
4328
4272
  node_process_1.default.stdin.resume();
4329
4273
  }
4330
4274
  console.log(`PlayDrop Worker started as "${workerName}" (${target}).`);
4331
- await sendWorkerHealthAlertSafely({ state: 'started', env, workerName });
4332
4275
  beginProviderTelemetryRefresh();
4333
4276
  // Marks the session expired, kills any in-flight agent child, and lets the
4334
4277
  // run loop finish the task lifecycle before the worker stops non-zero.
@@ -5302,26 +5245,11 @@ async function startWorker(options = {}) {
5302
5245
  return;
5303
5246
  }
5304
5247
  if (shuttingDown) {
5305
- await sendWorkerHealthAlertSafely({
5306
- state: 'stopped',
5307
- env,
5308
- workerName,
5309
- taskId: firstActiveTaskId(),
5310
- detail: shutdownSignal,
5311
- });
5312
5248
  node_process_1.default.exitCode = shutdownSignal === 'SIGINT' ? 130 : 143;
5313
5249
  return;
5314
5250
  }
5315
5251
  }
5316
5252
  catch (error) {
5317
- crashed = true;
5318
- await sendWorkerHealthAlertSafely({
5319
- state: 'crashed',
5320
- env,
5321
- workerName,
5322
- taskId: firstActiveTaskId(),
5323
- detail: error instanceof Error ? error.message : String(error),
5324
- });
5325
5253
  throw error;
5326
5254
  }
5327
5255
  finally {
@@ -5334,14 +5262,6 @@ async function startWorker(options = {}) {
5334
5262
  node_process_1.default.stdin.off('end', stdinShutdownHandler);
5335
5263
  node_process_1.default.stdin.off('close', stdinShutdownHandler);
5336
5264
  }
5337
- if (!crashed) {
5338
- await sendWorkerHealthAlertSafely({
5339
- state: 'stopped',
5340
- env,
5341
- workerName,
5342
- taskId: firstActiveTaskId(),
5343
- });
5344
- }
5345
5265
  clearWorkerRuntimeState(target);
5346
5266
  }
5347
5267
  }
package/dist/index.js CHANGED
@@ -45,6 +45,7 @@ const changelog_1 = require("./commands/changelog");
45
45
  const publicPages_1 = require("./commands/publicPages");
46
46
  const upgrade_1 = require("./commands/upgrade");
47
47
  const tweaks_1 = require("./commands/tweaks");
48
+ const chat_1 = require("./commands/chat");
48
49
  const notes_1 = require("./commands/notes");
49
50
  const worker_1 = require("./commands/worker");
50
51
  const messages_1 = require("./messages");
@@ -378,6 +379,48 @@ registerLogoutCommand(auth);
378
379
  registerWhoamiCommand(auth);
379
380
  registerAccountsCommand(auth);
380
381
  registerUseAccountCommand(auth);
382
+ function addChatReadOptions(command) {
383
+ return command
384
+ .option('--env <env>', 'Resolve this command against one logged-in environment')
385
+ .option('--json', 'Output JSON');
386
+ }
387
+ function addChatWriteOptions(command, allowClientKey = true) {
388
+ command
389
+ .option('--text <text>', 'Message text')
390
+ .option('--text-file <path>', 'Read message text from a file')
391
+ .option('--file <path>', 'Attach a file', collectRepeatedOption, [])
392
+ .option('--link <url>', 'Attach an HTTPS link', collectRepeatedOption, [])
393
+ .option('--event-type <type>', 'Structured event type')
394
+ .option('--event-data-file <path>', 'Read structured event JSON from a file')
395
+ .option('--as-system', 'Publish as playdrop-system (admin only)');
396
+ if (allowClientKey)
397
+ command.option('--client-key <key>', 'Idempotency key within the channel');
398
+ return addChatReadOptions(command);
399
+ }
400
+ const chat = program.command('chat').description('Read and write PlayDrop Chat');
401
+ addChatReadOptions(chat.command('channels').description('List accessible channels'))
402
+ .action(chat_1.listChatChannels);
403
+ addChatReadOptions(chat.command('read <channel>').description('Read channel messages'))
404
+ .action(chat_1.readChatChannel);
405
+ addChatReadOptions(chat.command('thread <message-id>').description('Read a message thread'))
406
+ .action(chat_1.readChatThread);
407
+ addChatWriteOptions(chat.command('send <channel>').description('Send a channel message'))
408
+ .action(chat_1.sendChatMessage);
409
+ addChatWriteOptions(chat.command('reply <message-id>').description('Reply to a root message'))
410
+ .action(chat_1.replyChatMessage);
411
+ addChatWriteOptions(chat.command('edit <message-id>').description('Edit a message'), false)
412
+ .action(chat_1.editChatMessage);
413
+ addChatReadOptions(chat.command('react <message-id> <emoji>').description('Add a reaction'))
414
+ .action(chat_1.reactChatMessage);
415
+ addChatReadOptions(chat.command('unreact <message-id> <emoji>').description('Remove a reaction'))
416
+ .action(chat_1.unreactChatMessage);
417
+ const chatAccess = chat.command('access').description('Manage channel access');
418
+ addChatReadOptions(chatAccess.command('grant <channel> <username>').description('Grant channel access').option('--read-only', 'Grant read access without write access'))
419
+ .action(chat_1.grantChatAccess);
420
+ addChatReadOptions(chatAccess.command('revoke <channel> <username>').description('Revoke channel access'))
421
+ .action(chat_1.revokeChatAccess);
422
+ addChatReadOptions(chat.command('visibility <channel> <visibility>').description('Set public or admin-only visibility'))
423
+ .action(chat_1.setChatVisibility);
381
424
  program
382
425
  .command('doctor')
383
426
  .description('Check PlayDrop setup health')