t3-code-android-nightly/infra/relay/scripts/android-push-smoke.ts
Julius Marminge 194c73f3f9
chore(deps): upgrade Effect to stable 4.0.1 (#16138)
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-05 13:15:14 -07:00

152 lines
5.3 KiB
TypeScript

// @effect-diagnostics nodeBuiltinImport:off - This developer command reads local credential files.
import * as NodeFSP from "node:fs/promises";
import * as NodeRuntime from "@effect/platform-node/NodeRuntime";
import * as Effect from "effect/Effect";
import * as Clock from "effect/Clock";
import * as Layer from "effect/Layer";
import * as Redacted from "effect/Redacted";
import * as Schema from "effect/Schema";
import * as FetchHttpClient from "effect/http/FetchHttpClient";
import * as RelayConfiguration from "../src/Config.ts";
import * as WebCrypto from "../src/WebCrypto.ts";
import * as FcmAssertionSigner from "../src/agentActivity/FcmAssertionSigner.ts";
import * as FcmClient from "../src/agentActivity/FcmClient.ts";
const Device = Schema.Struct({
token: Schema.NonEmptyString,
deviceId: Schema.NonEmptyString,
userId: Schema.NonEmptyString,
packageName: Schema.NonEmptyString,
deepLink: Schema.optional(Schema.NonEmptyString),
});
const decodeDevice = Schema.decodeUnknownEffect(Schema.fromJsonString(Device));
const Phase = Schema.Literals(["running", "approval", "input", "completed", "failed", "end"]);
const decodePhase = Schema.decodeUnknownEffect(Phase);
class SmokeUsageError extends Schema.TaggedError<SmokeUsageError>()("SmokeUsageError", {}) {
override get message() {
return "Usage: node scripts/android-push-smoke.ts <service-account.json> <device.json> <running|approval|input|completed|failed|end>";
}
}
class SmokeCredentialReadError extends Schema.TaggedError<SmokeCredentialReadError>()(
"SmokeCredentialReadError",
{ cause: Schema.Defect() },
) {
override get message() {
return "Could not read service-account file.";
}
}
class SmokeDeviceReadError extends Schema.TaggedError<SmokeDeviceReadError>()(
"SmokeDeviceReadError",
{ cause: Schema.Defect() },
) {
override get message() {
return "Could not read device file.";
}
}
class SmokeUnregisteredDeviceError extends Schema.TaggedError<SmokeUnregisteredDeviceError>()(
"SmokeUnregisteredDeviceError",
{},
) {
override get message() {
return "This device token is no longer registered with Firebase.";
}
}
const main = Effect.gen(function* () {
const [credentialPath, devicePath, phaseArg] = process.argv.slice(2);
if (!credentialPath || !devicePath || !phaseArg) return yield* new SmokeUsageError({});
const phase = yield* decodePhase(phaseArg);
const credentials = yield* Effect.tryPromise({
try: () => NodeFSP.readFile(credentialPath, "utf8"),
catch: (cause) => new SmokeCredentialReadError({ cause }),
});
const device = yield* Effect.tryPromise({
try: () => NodeFSP.readFile(devicePath, "utf8"),
catch: (cause) => new SmokeDeviceReadError({ cause }),
}).pipe(Effect.flatMap(decodeDevice));
const title =
phase === "completed"
? "Agent finished"
: phase === "failed"
? "Agent failed"
: phase === "approval"
? "Approval needed"
: phase === "input"
? "Input needed"
: null;
const active = phase === "running" || phase === "approval" || phase === "input";
const now = yield* Clock.currentTimeMillis;
const config: RelayConfiguration.RelayConfiguration["Service"] = {
relayIssuer: "http://localhost",
fcmServiceAccount: Redacted.make(credentials),
apns: null,
clerkSecretKey: Redacted.make(""),
clerkPublishableKey: "",
clerkJwtAudience: "",
apnsDeliveryJobSigningSecret: Redacted.make(""),
cloudMintPrivateKey: Redacted.make(""),
cloudMintPublicKey: "",
managedEndpointBaseDomain: undefined,
managedEndpointNamespace: undefined,
};
const result = yield* FcmClient.FcmClient.pipe(
Effect.flatMap((client) =>
client.send({
token: device.token,
packageName: device.packageName,
alert: title !== null,
data: {
t3_kind: "agent_activity",
device_id: device.deviceId,
user_id: device.userId,
updated_at: String(now),
active: String(active),
activity_title: active
? "1 active agent"
: phase === "failed"
? "Agent work failed"
: "Agent work completed",
activity_expires_at: String(
phase === "end" ? 0 : now + (active ? 2 * 60 * 60 * 1000 : 15 * 60 * 1000),
),
activity_body: "Android notification test",
activity_path: device.deepLink ?? "/",
...(title
? {
alert_id: `smoke-${now}`,
alert_title: title,
alert_body: "T3 Code Android push test",
alert_path: device.deepLink ?? "/",
}
: {}),
},
}),
),
Effect.provide(
FcmClient.layer.pipe(
Layer.provide(
FcmAssertionSigner.layer.pipe(
Layer.provide(Layer.succeed(WebCrypto.WebCrypto, { subtle: globalThis.crypto.subtle })),
),
),
Layer.provide(
Layer.mergeAll(
Layer.succeed(RelayConfiguration.RelayConfiguration, config),
FetchHttpClient.layer,
),
),
),
),
);
if (result.unregistered) return yield* new SmokeUnregisteredDeviceError({});
yield* Effect.logInfo(
`Firebase accepted the ${phase} notification. Verify delivery on the device.`,
);
});
NodeRuntime.runMain(main);