@playdrop/playdrop-cli 0.15.28 → 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.
- package/config/client-meta.json +4 -4
- package/dist/commands/chat.d.ts +27 -0
- package/dist/commands/chat.js +224 -0
- package/dist/commands/upgrade.js +1 -1
- package/dist/commands/worker/provider-telemetry.d.ts +4 -0
- package/dist/commands/worker/provider-telemetry.js +30 -3
- package/dist/commands/worker.d.ts +0 -8
- package/dist/commands/worker.js +0 -80
- package/dist/index.js +43 -0
- package/node_modules/@playdrop/api-client/dist/client.d.ts +25 -1
- package/node_modules/@playdrop/api-client/dist/client.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/client.js +14 -0
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts +60 -0
- package/node_modules/@playdrop/api-client/dist/domains/chat.d.ts.map +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/chat.js +208 -0
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts +2 -1
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/free-credits.js +11 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/payments.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/domains/payments.js +10 -0
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts +26 -0
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.d.ts.map +1 -0
- package/node_modules/@playdrop/api-client/dist/domains/playtrade.js +91 -0
- package/node_modules/@playdrop/api-client/dist/index.d.ts +9 -1
- package/node_modules/@playdrop/api-client/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/api-client/dist/index.js +35 -0
- package/node_modules/@playdrop/config/client-meta.json +4 -4
- package/node_modules/@playdrop/types/dist/api.d.ts +41 -5
- package/node_modules/@playdrop/types/dist/api.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/api.js +33 -0
- package/node_modules/@playdrop/types/dist/app-review.d.ts +1 -1
- package/node_modules/@playdrop/types/dist/app-review.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/app-review.js +1 -0
- package/node_modules/@playdrop/types/dist/attachment-policy.d.ts +8 -0
- package/node_modules/@playdrop/types/dist/attachment-policy.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/attachment-policy.js +58 -0
- package/node_modules/@playdrop/types/dist/chat.d.ts +104 -0
- package/node_modules/@playdrop/types/dist/chat.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/chat.js +40 -0
- package/node_modules/@playdrop/types/dist/index.d.ts +3 -0
- package/node_modules/@playdrop/types/dist/index.d.ts.map +1 -1
- package/node_modules/@playdrop/types/dist/index.js +7 -1
- package/node_modules/@playdrop/types/dist/playtrade.d.ts +72 -0
- package/node_modules/@playdrop/types/dist/playtrade.d.ts.map +1 -0
- package/node_modules/@playdrop/types/dist/playtrade.js +27 -0
- package/node_modules/@playdrop/types/dist/version.d.ts +6 -0
- package/node_modules/@playdrop/types/dist/version.d.ts.map +1 -1
- package/package.json +1 -1
package/config/client-meta.json
CHANGED
|
@@ -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
|
+
}
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -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
|
|
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
|
|
100
|
-
resetsAt
|
|
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
|
|
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;
|
package/dist/commands/worker.js
CHANGED
|
@@ -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')
|