diff --git a/CHANGELOG.md b/CHANGELOG.md
index ea34fa9ef7..f7e1ac6a78 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,6 +16,12 @@
 
 ### Client
 - Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように
+- Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正
+- Enhance: チャンネルノートのピン留めをノートのメニューからできるよ
+
+### Server
+- Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました
+- Enhance: ActivityPub Deliver queueでBodyを事前処理するように (#12916)
 
 ## 2023.12.2
 
diff --git a/ROADMAP.md b/ROADMAP.md
index 3077c41e73..509ecb9fe7 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -6,6 +6,7 @@ Also, the later tasks are more indefinite and are subject to change as developme
 This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development.
 
 - ~~Make the number of type errors zero (backend)~~ → Done ✔️
+- Make the number of type errors zero (frontend)
 - Improve CI
 	- ~~Fix tests~~ → Done ✔️
 	- Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 9af2dcca3b..f7a1f72256 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -672,6 +672,7 @@ export interface Locale {
     "other": string;
     "regenerateLoginToken": string;
     "regenerateLoginTokenDescription": string;
+    "theKeywordWhenSearchingForCustomEmoji": string;
     "setMultipleBySeparatingWithSpace": string;
     "fileIdOrUrl": string;
     "behavior": string;
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index 547cf4f603..1ee126351d 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -669,6 +669,7 @@ useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使
 other: "その他"
 regenerateLoginToken: "ログイントークンを再生成"
 regenerateLoginTokenDescription: "ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。"
+theKeywordWhenSearchingForCustomEmoji: "カスタム絵文字を検索する時のキーワードになります。"
 setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できます。"
 fileIdOrUrl: "ファイルIDまたはURL"
 behavior: "動作"
diff --git a/packages/backend/src/core/QueueService.ts b/packages/backend/src/core/QueueService.ts
index be378a899b..4f99dee64e 100644
--- a/packages/backend/src/core/QueueService.ts
+++ b/packages/backend/src/core/QueueService.ts
@@ -16,6 +16,7 @@ import type { DbQueue, DeliverQueue, EndedPollNotificationQueue, InboxQueue, Obj
 import type { DbJobData, DeliverJobData, RelationshipJobData, ThinUser } from '../queue/types.js';
 import type httpSignature from '@peertube/http-signature';
 import type * as Bull from 'bullmq';
+import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
 
 @Injectable()
 export class QueueService {
@@ -74,11 +75,15 @@ export class QueueService {
 		if (content == null) return null;
 		if (to == null) return null;
 
+		const contentBody = JSON.stringify(content);
+		const digest = ApRequestCreator.createDigest(contentBody);
+
 		const data: DeliverJobData = {
 			user: {
 				id: user.id,
 			},
-			content,
+			content: contentBody,
+			digest,
 			to,
 			isSharedInbox,
 		};
@@ -103,6 +108,8 @@ export class QueueService {
 	@bindThis
 	public async deliverMany(user: ThinUser, content: IActivity | null, inboxes: Map<string, boolean>) {
 		if (content == null) return null;
+		const contentBody = JSON.stringify(content);
+		const digest = ApRequestCreator.createDigest(contentBody);
 
 		const opts = {
 			attempts: this.config.deliverJobMaxAttempts ?? 12,
@@ -117,7 +124,8 @@ export class QueueService {
 			name: d[0],
 			data: {
 				user,
-				content,
+				content: contentBody,
+				digest,
 				to: d[0],
 				isSharedInbox: d[1],
 			} as DeliverJobData,
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index baaab67e48..a0c63bdbf7 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -97,6 +97,8 @@ export class ApInboxService {
 				} catch (err) {
 					if (err instanceof Error || typeof err === 'string') {
 						this.logger.error(err);
+					} else {
+						throw err;
 					}
 				}
 			}
@@ -256,7 +258,7 @@ export class ApInboxService {
 
 		const targetUri = getApId(activity.object);
 
-		this.announceNote(actor, activity, targetUri);
+		await this.announceNote(actor, activity, targetUri);
 	}
 
 	@bindThis
@@ -288,7 +290,7 @@ export class ApInboxService {
 			} catch (err) {
 				// 対象が4xxならスキップ
 				if (err instanceof StatusError) {
-					if (err.isClientError) {
+					if (!err.isRetryable) {
 						this.logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`);
 						return;
 					}
@@ -373,7 +375,7 @@ export class ApInboxService {
 		});
 
 		if (isPost(object)) {
-			this.createNote(resolver, actor, object, false, activity);
+			await this.createNote(resolver, actor, object, false, activity);
 		} else {
 			this.logger.warn(`Unknown type: ${getApType(object)}`);
 		}
@@ -404,7 +406,7 @@ export class ApInboxService {
 			await this.apNoteService.createNote(note, resolver, silent);
 			return 'ok';
 		} catch (err) {
-			if (err instanceof StatusError && err.isClientError) {
+			if (err instanceof StatusError && !err.isRetryable) {
 				return `skip ${err.statusCode}`;
 			} else {
 				throw err;
diff --git a/packages/backend/src/core/activitypub/ApRequestService.ts b/packages/backend/src/core/activitypub/ApRequestService.ts
index b59ce5241f..e165c5e960 100644
--- a/packages/backend/src/core/activitypub/ApRequestService.ts
+++ b/packages/backend/src/core/activitypub/ApRequestService.ts
@@ -34,9 +34,9 @@ type PrivateKey = {
 };
 
 export class ApRequestCreator {
-	static createSignedPost(args: { key: PrivateKey, url: string, body: string, additionalHeaders: Record<string, string> }): Signed {
+	static createSignedPost(args: { key: PrivateKey, url: string, body: string, digest?: string, additionalHeaders: Record<string, string> }): Signed {
 		const u = new URL(args.url);
-		const digestHeader = `SHA-256=${crypto.createHash('sha256').update(args.body).digest('base64')}`;
+		const digestHeader = args.digest ?? this.createDigest(args.body);
 
 		const request: Request = {
 			url: u.href,
@@ -59,6 +59,10 @@ export class ApRequestCreator {
 		};
 	}
 
+	static createDigest(body: string) {
+		return `SHA-256=${crypto.createHash('sha256').update(body).digest('base64')}`;
+	}
+
 	static createSignedGet(args: { key: PrivateKey, url: string, additionalHeaders: Record<string, string> }): Signed {
 		const u = new URL(args.url);
 
@@ -145,8 +149,8 @@ export class ApRequestService {
 	}
 
 	@bindThis
-	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown): Promise<void> {
-		const body = JSON.stringify(object);
+	public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown, digest?: string): Promise<void> {
+		const body = typeof object === 'string' ? object : JSON.stringify(object);
 
 		const keypair = await this.userKeypairService.getUserKeypair(user.id);
 
@@ -157,6 +161,7 @@ export class ApRequestService {
 			},
 			url,
 			body,
+			digest,
 			additionalHeaders: {
 			},
 		});
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 05d5ca15db..e3eccd5405 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -216,7 +216,7 @@ export class ApNoteService {
 					return { status: 'ok', res };
 				} catch (e) {
 					return {
-						status: (e instanceof StatusError && e.isClientError) ? 'permerror' : 'temperror',
+						status: (e instanceof StatusError && !e.isRetryable) ? 'permerror' : 'temperror',
 					};
 				}
 			};
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index c49dad8e79..1777e2cf54 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -351,6 +351,7 @@ export class NoteEntityService implements OnModuleInit {
 				color: channel.color,
 				isSensitive: channel.isSensitive,
 				allowRenoteToExternal: channel.allowRenoteToExternal,
+				userId: channel.userId,
 			} : undefined,
 			mentions: note.mentions.length > 0 ? note.mentions : undefined,
 			uri: note.uri ?? undefined,
diff --git a/packages/backend/src/logger.ts b/packages/backend/src/logger.ts
index 5c10559ec6..0a19036c97 100644
--- a/packages/backend/src/logger.ts
+++ b/packages/backend/src/logger.ts
@@ -71,8 +71,11 @@ export default class Logger {
 		let log = `${l} ${worker}\t[${contexts.join(' ')}]\t${m}`;
 		if (envOption.withLogTime) log = chalk.gray(time) + ' ' + log;
 
-		console.log(important ? chalk.bold(log) : log);
-		if (level === 'error' && data) console.log(data);
+		const args: unknown[] = [important ? chalk.bold(log) : log];
+		if (data != null) {
+			args.push(data);
+		}
+		console.log(...args);
 	}
 
 	@bindThis
diff --git a/packages/backend/src/misc/status-error.ts b/packages/backend/src/misc/status-error.ts
index 4285685d24..be213088a8 100644
--- a/packages/backend/src/misc/status-error.ts
+++ b/packages/backend/src/misc/status-error.ts
@@ -7,6 +7,7 @@ export class StatusError extends Error {
 	public statusCode: number;
 	public statusMessage?: string;
 	public isClientError: boolean;
+	public isRetryable: boolean;
 
 	constructor(message: string, statusCode: number, statusMessage?: string) {
 		super(message);
@@ -14,5 +15,6 @@ export class StatusError extends Error {
 		this.statusCode = statusCode;
 		this.statusMessage = statusMessage;
 		this.isClientError = typeof this.statusCode === 'number' && this.statusCode >= 400 && this.statusCode < 500;
+		this.isRetryable = !this.isClientError || this.statusCode === 429;
 	}
 }
diff --git a/packages/backend/src/models/json-schema/note.ts b/packages/backend/src/models/json-schema/note.ts
index b663f1f727..f1cb5d83cb 100644
--- a/packages/backend/src/models/json-schema/note.ts
+++ b/packages/backend/src/models/json-schema/note.ts
@@ -152,6 +152,10 @@ export const packedNoteSchema = {
 					type: 'boolean',
 					optional: false, nullable: false,
 				},
+				userId: {
+					type: 'string',
+					optional: false, nullable: true,
+				},
 			},
 		},
 		localOnly: {
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index 4a1d9f28b4..64c3445552 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -72,7 +72,7 @@ export class DeliverProcessorService {
 		}
 
 		try {
-			await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content);
+			await this.apRequestService.signedPost(job.data.user, job.data.to, job.data.content, job.data.digest);
 
 			// Update stats
 			this.federatedInstanceService.fetch(host).then(i => {
@@ -111,7 +111,7 @@ export class DeliverProcessorService {
 
 			if (res instanceof StatusError) {
 				// 4xx
-				if (res.isClientError) {
+				if (!res.isRetryable) {
 					// 相手が閉鎖していることを明示しているため、配送停止する
 					if (job.data.isSharedInbox && res.statusCode === 410) {
 						this.federatedInstanceService.fetch(host).then(i => {
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index 89d4ea503e..50e7a1631e 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -85,7 +85,7 @@ export class InboxProcessorService {
 			} catch (err) {
 				// 対象が4xxならスキップ
 				if (err instanceof StatusError) {
-					if (err.isClientError) {
+					if (!err.isRetryable) {
 						throw new Bull.UnrecoverableError(`skip: Ignored deleted actors on both ends ${activity.actor} - ${err.statusCode}`);
 					}
 					throw new Error(`Error in actor ${activity.actor} - ${err.statusCode}`);
diff --git a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts
index a41f5565c8..7a0d533846 100644
--- a/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/WebhookDeliverProcessorService.ts
@@ -71,7 +71,7 @@ export class WebhookDeliverProcessorService {
 
 			if (res instanceof StatusError) {
 				// 4xx
-				if (res.isClientError) {
+				if (!res.isRetryable) {
 					throw new Bull.UnrecoverableError(`${res.statusCode} ${res.statusMessage}`);
 				}
 
diff --git a/packages/backend/src/queue/types.ts b/packages/backend/src/queue/types.ts
index 9330c01528..5a9952e807 100644
--- a/packages/backend/src/queue/types.ts
+++ b/packages/backend/src/queue/types.ts
@@ -15,7 +15,9 @@ export type DeliverJobData = {
 	/** Actor */
 	user: ThinUser;
 	/** Activity */
-	content: unknown;
+	content: string;
+	/** Digest header */
+	digest: string;
 	/** inbox URL to deliver */
 	to: string;
 	/** whether it is sharedInbox */
diff --git a/packages/backend/test/e2e/2fa.ts b/packages/backend/test/e2e/2fa.ts
index ed967d2620..dfed8b2fce 100644
--- a/packages/backend/test/e2e/2fa.ts
+++ b/packages/backend/test/e2e/2fa.ts
@@ -24,7 +24,7 @@ import type * as misskey from 'misskey-js';
 
 describe('2要素認証', () => {
 	let app: INestApplicationContext;
-	let alice: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
 
 	const config = loadConfig();
 	const password = 'test';
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index c0317f1435..9bac5122d4 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -37,7 +37,7 @@ describe('アンテナ', () => {
 	// - srcのenumにgroupが残っている
 	// - userGroupIdが残っている, isActiveがない
 	type Antenna = misskey.entities.Antenna | Packed<'Antenna'>;
-	type User = misskey.entities.MeSignup;
+	type User = misskey.entities.SignupResponse;
 	type Note = misskey.entities.Note;
 
 	// アンテナを作成できる最小のパラメタ
diff --git a/packages/backend/test/e2e/api-visibility.ts b/packages/backend/test/e2e/api-visibility.ts
index 33c8d03fdb..afe4f9c05a 100644
--- a/packages/backend/test/e2e/api-visibility.ts
+++ b/packages/backend/test/e2e/api-visibility.ts
@@ -24,15 +24,15 @@ describe('API visibility', () => {
 	describe('Note visibility', () => {
 		//#region vars
 		/** ヒロイン */
-		let alice: misskey.entities.MeSignup;
+		let alice: misskey.entities.SignupResponse;
 		/** フォロワー */
-		let follower: misskey.entities.MeSignup;
+		let follower: misskey.entities.SignupResponse;
 		/** 非フォロワー */
-		let other: misskey.entities.MeSignup;
+		let other: misskey.entities.SignupResponse;
 		/** 非フォロワーでもリプライやメンションをされた人 */
-		let target: misskey.entities.MeSignup;
+		let target: misskey.entities.SignupResponse;
 		/** specified mentionでmentionを飛ばされる人 */
-		let target2: misskey.entities.MeSignup;
+		let target2: misskey.entities.SignupResponse;
 
 		/** public-post */
 		let pub: any;
diff --git a/packages/backend/test/e2e/api.ts b/packages/backend/test/e2e/api.ts
index cf24228b83..ad351eebbb 100644
--- a/packages/backend/test/e2e/api.ts
+++ b/packages/backend/test/e2e/api.ts
@@ -13,9 +13,9 @@ import type * as misskey from 'misskey-js';
 
 describe('API', () => {
 	let app: INestApplicationContext;
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/block.ts b/packages/backend/test/e2e/block.ts
index 4445d9036c..25ff9f11ac 100644
--- a/packages/backend/test/e2e/block.ts
+++ b/packages/backend/test/e2e/block.ts
@@ -14,9 +14,9 @@ describe('Block', () => {
 	let app: INestApplicationContext;
 
 	// alice blocks bob
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/endpoints.ts b/packages/backend/test/e2e/endpoints.ts
index 2ef3434bca..bfd0b0272d 100644
--- a/packages/backend/test/e2e/endpoints.ts
+++ b/packages/backend/test/e2e/endpoints.ts
@@ -17,10 +17,10 @@ import type * as misskey from 'misskey-js';
 describe('Endpoints', () => {
 	let app: INestApplicationContext;
 
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
-	let dave: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
+	let dave: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts
index 251d662760..28affe7768 100644
--- a/packages/backend/test/e2e/fetch-resource.ts
+++ b/packages/backend/test/e2e/fetch-resource.ts
@@ -25,7 +25,7 @@ const JSON_UTF8 = 'application/json; charset=utf-8';
 describe('Webリソース', () => {
 	let app: INestApplicationContext;
 
-	let alice: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
 	let aliceUploadedFile: any;
 	let alicesPost: any;
 	let alicePage: any;
@@ -34,7 +34,7 @@ describe('Webリソース', () => {
 	let aliceGalleryPost: any;
 	let aliceChannel: any;
 
-	let bob: misskey.entities.MeSignup;
+	let bob: misskey.entities.SignupResponse;
 
 	type Request = {
 		path: string,
diff --git a/packages/backend/test/e2e/ff-visibility.ts b/packages/backend/test/e2e/ff-visibility.ts
index 1fbd45c741..4d323e14e7 100644
--- a/packages/backend/test/e2e/ff-visibility.ts
+++ b/packages/backend/test/e2e/ff-visibility.ts
@@ -13,8 +13,8 @@ import type * as misskey from 'misskey-js';
 describe('FF visibility', () => {
 	let app: INestApplicationContext;
 
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index b009ef124a..f7da66a27c 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -20,12 +20,12 @@ describe('Account Move', () => {
 	let url: URL;
 
 	let root: any;
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
-	let dave: misskey.entities.MeSignup;
-	let eve: misskey.entities.MeSignup;
-	let frank: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
+	let dave: misskey.entities.SignupResponse;
+	let eve: misskey.entities.SignupResponse;
+	let frank: misskey.entities.SignupResponse;
 
 	let Users: UsersRepository;
 
diff --git a/packages/backend/test/e2e/mute.ts b/packages/backend/test/e2e/mute.ts
index a4b57a1eba..3b5542dfe0 100644
--- a/packages/backend/test/e2e/mute.ts
+++ b/packages/backend/test/e2e/mute.ts
@@ -14,9 +14,9 @@ describe('Mute', () => {
 	let app: INestApplicationContext;
 
 	// alice mutes carol
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts
index 961df99cc2..8d33c63485 100644
--- a/packages/backend/test/e2e/note.ts
+++ b/packages/backend/test/e2e/note.ts
@@ -16,8 +16,8 @@ describe('Note', () => {
 	let app: INestApplicationContext;
 	let Notes: any;
 
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/oauth.ts b/packages/backend/test/e2e/oauth.ts
index 3a5e4ebdae..3ca1f8b542 100644
--- a/packages/backend/test/e2e/oauth.ts
+++ b/packages/backend/test/e2e/oauth.ts
@@ -75,7 +75,7 @@ function getMeta(html: string): { transactionId: string | undefined, clientName:
 	};
 }
 
-function fetchDecision(transactionId: string, user: misskey.entities.MeSignup, { cancel }: { cancel?: boolean } = {}): Promise<Response> {
+function fetchDecision(transactionId: string, user: misskey.entities.SignupResponse, { cancel }: { cancel?: boolean } = {}): Promise<Response> {
 	return fetch(new URL('/oauth/decision', host), {
 		method: 'post',
 		body: new URLSearchParams({
@@ -90,14 +90,14 @@ function fetchDecision(transactionId: string, user: misskey.entities.MeSignup, {
 	});
 }
 
-async function fetchDecisionFromResponse(response: Response, user: misskey.entities.MeSignup, { cancel }: { cancel?: boolean } = {}): Promise<Response> {
+async function fetchDecisionFromResponse(response: Response, user: misskey.entities.SignupResponse, { cancel }: { cancel?: boolean } = {}): Promise<Response> {
 	const { transactionId } = getMeta(await response.text());
 	assert.ok(transactionId);
 
 	return await fetchDecision(transactionId, user, { cancel });
 }
 
-async function fetchAuthorizationCode(user: misskey.entities.MeSignup, scope: string, code_challenge: string): Promise<{ client: AuthorizationCode, code: string }> {
+async function fetchAuthorizationCode(user: misskey.entities.SignupResponse, scope: string, code_challenge: string): Promise<{ client: AuthorizationCode, code: string }> {
 	const client = new AuthorizationCode(clientConfig);
 
 	const response = await fetch(client.authorizeURL({
@@ -150,8 +150,8 @@ describe('OAuth', () => {
 	let app: INestApplicationContext;
 	let fastify: FastifyInstance;
 
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
 
 	let sender: (reply: FastifyReply) => void;
 
diff --git a/packages/backend/test/e2e/renote-mute.ts b/packages/backend/test/e2e/renote-mute.ts
index 7d57ba17b6..fededdff32 100644
--- a/packages/backend/test/e2e/renote-mute.ts
+++ b/packages/backend/test/e2e/renote-mute.ts
@@ -14,9 +14,9 @@ describe('Renote Mute', () => {
 	let app: INestApplicationContext;
 
 	// alice mutes carol
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/streaming.ts b/packages/backend/test/e2e/streaming.ts
index 288c54bdbc..a447ba94ae 100644
--- a/packages/backend/test/e2e/streaming.ts
+++ b/packages/backend/test/e2e/streaming.ts
@@ -32,15 +32,15 @@ describe('Streaming', () => {
 
 	describe('Streaming', () => {
 		// Local users
-		let ayano: misskey.entities.MeSignup;
-		let kyoko: misskey.entities.MeSignup;
-		let chitose: misskey.entities.MeSignup;
-		let kanako: misskey.entities.MeSignup;
+		let ayano: misskey.entities.SignupResponse;
+		let kyoko: misskey.entities.SignupResponse;
+		let chitose: misskey.entities.SignupResponse;
+		let kanako: misskey.entities.SignupResponse;
 
 		// Remote users
-		let akari: misskey.entities.MeSignup;
-		let chinatsu: misskey.entities.MeSignup;
-		let takumi: misskey.entities.MeSignup;
+		let akari: misskey.entities.SignupResponse;
+		let chinatsu: misskey.entities.SignupResponse;
+		let takumi: misskey.entities.SignupResponse;
 
 		let kyokoNote: any;
 		let kanakoNote: any;
diff --git a/packages/backend/test/e2e/thread-mute.ts b/packages/backend/test/e2e/thread-mute.ts
index 0e487976dc..5c68e2b150 100644
--- a/packages/backend/test/e2e/thread-mute.ts
+++ b/packages/backend/test/e2e/thread-mute.ts
@@ -13,9 +13,9 @@ import type * as misskey from 'misskey-js';
 describe('Note thread mute', () => {
 	let app: INestApplicationContext;
 
-	let alice: misskey.entities.MeSignup;
-	let bob: misskey.entities.MeSignup;
-	let carol: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
+	let bob: misskey.entities.SignupResponse;
+	let carol: misskey.entities.SignupResponse;
 
 	beforeAll(async () => {
 		app = await startServer();
diff --git a/packages/backend/test/e2e/user-notes.ts b/packages/backend/test/e2e/user-notes.ts
index b5f00a6327..4f2e7c4cf3 100644
--- a/packages/backend/test/e2e/user-notes.ts
+++ b/packages/backend/test/e2e/user-notes.ts
@@ -13,7 +13,7 @@ import type * as misskey from 'misskey-js';
 describe('users/notes', () => {
 	let app: INestApplicationContext;
 
-	let alice: misskey.entities.MeSignup;
+	let alice: misskey.entities.SignupResponse;
 	let jpgNote: any;
 	let pngNote: any;
 	let jpgPngNote: any;
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 3b944046b7..864779fd9d 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -45,7 +45,6 @@
 		"escape-regexp": "0.0.1",
 		"estree-walker": "3.0.3",
 		"eventemitter3": "5.0.1",
-		"gsap": "3.12.4",
 		"idb-keyval": "6.2.1",
 		"insert-text-at-cursor": "0.3.0",
 		"is-file-animated": "1.0.2",
diff --git a/packages/frontend/src/account.ts b/packages/frontend/src/account.ts
index f23fb804c5..b01a80d137 100644
--- a/packages/frontend/src/account.ts
+++ b/packages/frontend/src/account.ts
@@ -11,7 +11,8 @@ import { miLocalStorage } from '@/local-storage.js';
 import { MenuButton } from '@/types/menu.js';
 import { del, get, set } from '@/scripts/idb-proxy.js';
 import { apiUrl } from '@/config.js';
-import { waiting, api, popup, popupMenu, success, alert } from '@/os.js';
+import { waiting, popup, popupMenu, success, alert } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { unisonReload, reloadChannel } from '@/scripts/unison-reload.js';
 
 // TODO: 他のタブと永続化されたstateを同期
@@ -23,9 +24,14 @@ const accountData = miLocalStorage.getItem('account');
 // TODO: 外部からはreadonlyに
 export const $i = accountData ? reactive(JSON.parse(accountData) as Account) : null;
 
-export const iAmModerator = $i != null && ($i.isAdmin || $i.isModerator);
+export const iAmModerator = $i != null && ($i.isAdmin === true || $i.isModerator === true);
 export const iAmAdmin = $i != null && $i.isAdmin;
 
+export function signinRequired() {
+	if ($i == null) throw new Error('signin required');
+	return $i;
+}
+
 export let notesCount = $i == null ? 0 : $i.notesCount;
 export function incNotesCount() {
 	notesCount++;
@@ -246,7 +252,7 @@ export async function openAccountMenu(opts: {
 	}
 
 	const storedAccounts = await getAccounts().then(accounts => accounts.filter(x => x.id !== $i.id));
-	const accountsPromise = api('users/show', { userIds: storedAccounts.map(x => x.id) });
+	const accountsPromise = misskeyApi('users/show', { userIds: storedAccounts.map(x => x.id) });
 
 	function createItem(account: Misskey.entities.UserDetailed) {
 		return {
diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts
index 25d2b3c15f..20950add80 100644
--- a/packages/frontend/src/cache.ts
+++ b/packages/frontend/src/cache.ts
@@ -5,9 +5,9 @@
 
 import * as Misskey from 'misskey-js';
 import { Cache } from '@/scripts/cache.js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
-export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => api('clips/list'));
-export const rolesCache = new Cache(1000 * 60 * 30, () => api('admin/roles/list'));
-export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => api('users/lists/list'));
-export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => api('antennas/list'));
+export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, () => misskeyApi('clips/list'));
+export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list'));
+export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list'));
+export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list'));
diff --git a/packages/frontend/src/components/MkAccountMoved.vue b/packages/frontend/src/components/MkAccountMoved.vue
index 155d9fe3a9..a5b43d3c6c 100644
--- a/packages/frontend/src/components/MkAccountMoved.vue
+++ b/packages/frontend/src/components/MkAccountMoved.vue
@@ -17,7 +17,7 @@ import * as Misskey from 'misskey-js';
 import MkMention from './MkMention.vue';
 import { i18n } from '@/i18n.js';
 import { host as localHost } from '@/config.js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const user = ref<Misskey.entities.UserLite>();
 
@@ -25,7 +25,7 @@ const props = defineProps<{
 	movedTo: string; // user id
 }>();
 
-api('users/show', { userId: props.movedTo }).then(u => user.value = u);
+misskeyApi('users/show', { userId: props.movedTo }).then(u => user.value = u);
 </script>
 
 <style lang="scss" module>
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index 213a97ee1f..ff8a9fa1a5 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -55,6 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { onMounted, ref, computed } from 'vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { ACHIEVEMENT_TYPES, ACHIEVEMENT_BADGES, claimAchievement } from '@/scripts/achievements.js';
 
@@ -71,7 +72,7 @@ const achievements = ref<Misskey.entities.UsersAchievementsResponse | null>(null
 const lockedAchievements = computed(() => ACHIEVEMENT_TYPES.filter(x => !(achievements.value ?? []).some(a => a.name === x)));
 
 function fetch() {
-	os.api('users/achievements', { userId: props.user.id }).then(res => {
+	misskeyApi('users/achievements', { userId: props.user.id }).then(res => {
 		achievements.value = [];
 		for (const t of ACHIEVEMENT_TYPES) {
 			const a = res.find(x => x.name === t);
diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue
index 22cc4b7cef..7a99f73bff 100644
--- a/packages/frontend/src/components/MkAnnouncementDialog.vue
+++ b/packages/frontend/src/components/MkAnnouncementDialog.vue
@@ -25,6 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkModal from '@/components/MkModal.vue';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
@@ -49,7 +50,7 @@ async function ok() {
 	}
 
 	modal.value?.close();
-	os.api('i/read-announcement', { announcementId: props.announcement.id });
+	misskeyApi('i/read-announcement', { announcementId: props.announcement.id });
 	updateAccount({
 		unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== props.announcement.id),
 	});
diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue
index 494d120a93..49884c705f 100644
--- a/packages/frontend/src/components/MkAutocomplete.vue
+++ b/packages/frontend/src/components/MkAutocomplete.vue
@@ -45,6 +45,7 @@ import contains from '@/scripts/contains.js';
 import { char2twemojiFilePath, char2fluentEmojiFilePath } from '@/scripts/emoji-base.js';
 import { acct } from '@/filters/user.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { emojilist, getEmojiName } from '@/scripts/emojilist.js';
 import { i18n } from '@/i18n.js';
@@ -201,7 +202,7 @@ function exec() {
 			users.value = JSON.parse(cache);
 			fetching.value = false;
 		} else {
-			os.api('users/search-by-username-and-host', {
+			misskeyApi('users/search-by-username-and-host', {
 				username: props.q,
 				limit: 10,
 				detail: false,
@@ -224,7 +225,7 @@ function exec() {
 				hashtags.value = hashtags;
 				fetching.value = false;
 			} else {
-				os.api('hashtags/search', {
+				misskeyApi('hashtags/search', {
 					query: props.q,
 					limit: 30,
 				}).then(searchedHashtags => {
diff --git a/packages/frontend/src/components/MkAvatars.vue b/packages/frontend/src/components/MkAvatars.vue
index 5644a324cf..6ef4a7dfe2 100644
--- a/packages/frontend/src/components/MkAvatars.vue
+++ b/packages/frontend/src/components/MkAvatars.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const props = withDefaults(defineProps<{
 	userIds: string[];
@@ -27,7 +27,7 @@ const props = withDefaults(defineProps<{
 const users = ref<Misskey.entities.UserLite[]>([]);
 
 onMounted(async () => {
-	users.value = await os.api('users/show', {
+	users.value = await misskeyApi('users/show', {
 		userIds: props.userIds,
 	}) as unknown as Misskey.entities.UserLite[];
 });
diff --git a/packages/frontend/src/components/MkButton.vue b/packages/frontend/src/components/MkButton.vue
index 4a3f91122c..70de6a851a 100644
--- a/packages/frontend/src/components/MkButton.vue
+++ b/packages/frontend/src/components/MkButton.vue
@@ -131,6 +131,10 @@ function onMousedown(evt: MouseEvent): void {
 	box-sizing: border-box;
 	transition: background 0.1s ease;
 
+	&:hover {
+		text-decoration: none;
+	}
+
 	&:not(:disabled):hover {
 		background: var(--buttonHoverBg);
 	}
diff --git a/packages/frontend/src/components/MkChannelFollowButton.vue b/packages/frontend/src/components/MkChannelFollowButton.vue
index 41b02a7e3f..8a1d04e9c3 100644
--- a/packages/frontend/src/components/MkChannelFollowButton.vue
+++ b/packages/frontend/src/components/MkChannelFollowButton.vue
@@ -26,7 +26,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = withDefaults(defineProps<{
@@ -44,12 +44,12 @@ async function onClick() {
 
 	try {
 		if (isFollowing.value) {
-			await os.api('channels/unfollow', {
+			await misskeyApi('channels/unfollow', {
 				channelId: props.channel.id,
 			});
 			isFollowing.value = false;
 		} else {
-			await os.api('channels/follow', {
+			await misskeyApi('channels/follow', {
 				channelId: props.channel.id,
 			});
 			isFollowing.value = true;
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 85c10a2cf2..ed51e7aa75 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, ref, shallowRef, watch, PropType } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
@@ -282,7 +282,7 @@ const exportData = () => {
 };
 
 const fetchFederationChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/federation', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/federation', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Received',
@@ -332,7 +332,7 @@ const fetchFederationChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchApRequestChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/ap-request', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/ap-request', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'In',
@@ -354,7 +354,7 @@ const fetchApRequestChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'All',
@@ -401,7 +401,7 @@ const fetchNotesChart = async (type: string): Promise<typeof chartData> => {
 };
 
 const fetchNotesTotalChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/notes', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/notes', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Combined',
@@ -420,7 +420,7 @@ const fetchNotesTotalChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/users', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/users', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Combined',
@@ -448,7 +448,7 @@ const fetchUsersChart = async (total: boolean): Promise<typeof chartData> => {
 };
 
 const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/active-users', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/active-users', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Read & Write',
@@ -500,7 +500,7 @@ const fetchActiveUsersChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchDriveChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/drive', { limit: props.limit, span: props.span });
 	return {
 		bytes: true,
 		series: [{
@@ -536,7 +536,7 @@ const fetchDriveChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/drive', { limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/drive', { limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'All',
@@ -571,7 +571,7 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'In',
@@ -593,7 +593,7 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Users',
@@ -608,7 +608,7 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData
 };
 
 const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Notes',
@@ -623,7 +623,7 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData
 };
 
 const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Following',
@@ -646,7 +646,7 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> =
 };
 
 const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
 	return {
 		bytes: true,
 		series: [{
@@ -662,7 +662,7 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
 };
 
 const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Drive files',
@@ -677,7 +677,7 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char
 };
 
 const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
 	return {
 		series: [...(props.args?.withoutAll ? [] : [{
 			name: 'All',
@@ -709,7 +709,7 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Unique PV (user)',
@@ -736,7 +736,7 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Local',
@@ -751,7 +751,7 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
 	return {
 		series: [{
 			name: 'Local',
@@ -766,7 +766,7 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
 };
 
 const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
-	const raw = await os.apiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+	const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
 	return {
 		bytes: true,
 		series: [{
diff --git a/packages/frontend/src/components/MkCodeEditor.vue b/packages/frontend/src/components/MkCodeEditor.vue
index 6341c454ae..c8c3deb610 100644
--- a/packages/frontend/src/components/MkCodeEditor.vue
+++ b/packages/frontend/src/components/MkCodeEditor.vue
@@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div :class="$style.codeEditorScroller">
 			<textarea
 				ref="inputEl"
-				v-model="vModel"
+				v-model="v"
 				:class="[$style.textarea]"
 				:disabled="disabled"
 				:required="required"
@@ -58,7 +58,6 @@ const emit = defineEmits<{
 }>();
 
 const { modelValue } = toRefs(props);
-const vModel = ref<string>(modelValue.value ?? '');
 const v = ref<string>(modelValue.value ?? '');
 const focused = ref(false);
 const changed = ref(false);
@@ -79,15 +78,14 @@ const onKeydown = (ev: KeyboardEvent) => {
 
 	if (ev.code === 'Enter') {
 		const pos = inputEl.value?.selectionStart ?? 0;
-		const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length;
+		const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
 		if (pos === posEnd) {
-			const lines = vModel.value.slice(0, pos).split('\n');
+			const lines = v.value.slice(0, pos).split('\n');
 			const currentLine = lines[lines.length - 1];
 			const currentLineSpaces = currentLine.match(/^\s+/);
 			const posDelta = currentLineSpaces ? currentLineSpaces[0].length : 0;
 			ev.preventDefault();
-			vModel.value = vModel.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + vModel.value.slice(pos);
-			v.value = vModel.value;
+			v.value = v.value.slice(0, pos) + '\n' + (currentLineSpaces ? currentLineSpaces[0] : '') + v.value.slice(pos);
 			nextTick(() => {
 				inputEl.value?.setSelectionRange(pos + 1 + posDelta, pos + 1 + posDelta);
 			});
@@ -97,9 +95,8 @@ const onKeydown = (ev: KeyboardEvent) => {
 
 	if (ev.key === 'Tab') {
 		const pos = inputEl.value?.selectionStart ?? 0;
-		const posEnd = inputEl.value?.selectionEnd ?? vModel.value.length;
-		vModel.value = vModel.value.slice(0, pos) + '\t' + vModel.value.slice(posEnd);
-		v.value = vModel.value;
+		const posEnd = inputEl.value?.selectionEnd ?? v.value.length;
+		v.value = v.value.slice(0, pos) + '\t' + v.value.slice(posEnd);
 		nextTick(() => {
 			inputEl.value?.setSelectionRange(pos + 1, pos + 1);
 		});
diff --git a/packages/frontend/src/components/MkDrive.folder.vue b/packages/frontend/src/components/MkDrive.folder.vue
index da308876c0..b3569bd009 100644
--- a/packages/frontend/src/components/MkDrive.folder.vue
+++ b/packages/frontend/src/components/MkDrive.folder.vue
@@ -35,6 +35,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, defineAsyncComponent, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
 import { claimAchievement } from '@/scripts/achievements.js';
@@ -144,7 +145,7 @@ function onDrop(ev: DragEvent) {
 	if (driveFile != null && driveFile !== '') {
 		const file = JSON.parse(driveFile);
 		emit('removeFile', file.id);
-		os.api('drive/files/update', {
+		misskeyApi('drive/files/update', {
 			fileId: file.id,
 			folderId: props.folder.id,
 		});
@@ -160,7 +161,7 @@ function onDrop(ev: DragEvent) {
 		if (folder.id === props.folder.id) return;
 
 		emit('removeFolder', folder.id);
-		os.api('drive/folders/update', {
+		misskeyApi('drive/folders/update', {
 			folderId: folder.id,
 			parentId: props.folder.id,
 		}).then(() => {
@@ -214,7 +215,7 @@ function rename() {
 		default: props.folder.name,
 	}).then(({ canceled, result: name }) => {
 		if (canceled) return;
-		os.api('drive/folders/update', {
+		misskeyApi('drive/folders/update', {
 			folderId: props.folder.id,
 			name: name,
 		});
@@ -222,7 +223,7 @@ function rename() {
 }
 
 function deleteFolder() {
-	os.api('drive/folders/delete', {
+	misskeyApi('drive/folders/delete', {
 		folderId: props.folder.id,
 	}).then(() => {
 		if (defaultStore.state.uploadFolder === props.folder.id) {
diff --git a/packages/frontend/src/components/MkDrive.navFolder.vue b/packages/frontend/src/components/MkDrive.navFolder.vue
index 59458ad568..f66125e3d3 100644
--- a/packages/frontend/src/components/MkDrive.navFolder.vue
+++ b/packages/frontend/src/components/MkDrive.navFolder.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -112,7 +112,7 @@ function onDrop(ev: DragEvent) {
 	if (driveFile != null && driveFile !== '') {
 		const file = JSON.parse(driveFile);
 		emit('removeFile', file.id);
-		os.api('drive/files/update', {
+		misskeyApi('drive/files/update', {
 			fileId: file.id,
 			folderId: props.folder ? props.folder.id : null,
 		});
@@ -126,7 +126,7 @@ function onDrop(ev: DragEvent) {
 		// 移動先が自分自身ならreject
 		if (props.folder && folder.id === props.folder.id) return;
 		emit('removeFolder', folder.id);
-		os.api('drive/folders/update', {
+		misskeyApi('drive/folders/update', {
 			folderId: folder.id,
 			parentId: props.folder ? props.folder.id : null,
 		});
diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue
index 396defe300..a4706f9796 100644
--- a/packages/frontend/src/components/MkDrive.vue
+++ b/packages/frontend/src/components/MkDrive.vue
@@ -103,6 +103,7 @@ import XNavFolder from '@/components/MkDrive.navFolder.vue';
 import XFolder from '@/components/MkDrive.folder.vue';
 import XFile from '@/components/MkDrive.file.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
@@ -255,7 +256,7 @@ function onDrop(ev: DragEvent): any {
 		const file = JSON.parse(driveFile);
 		if (files.value.some(f => f.id === file.id)) return;
 		removeFile(file.id);
-		os.api('drive/files/update', {
+		misskeyApi('drive/files/update', {
 			fileId: file.id,
 			folderId: folder.value ? folder.value.id : null,
 		});
@@ -271,7 +272,7 @@ function onDrop(ev: DragEvent): any {
 		if (folder.value && droppedFolder.id === folder.value.id) return false;
 		if (folders.value.some(f => f.id === droppedFolder.id)) return false;
 		removeFolder(droppedFolder.id);
-		os.api('drive/folders/update', {
+		misskeyApi('drive/folders/update', {
 			folderId: droppedFolder.id,
 			parentId: folder.value ? folder.value.id : null,
 		}).then(() => {
@@ -308,7 +309,7 @@ function urlUpload() {
 		placeholder: i18n.ts.uploadFromUrlDescription,
 	}).then(({ canceled, result: url }) => {
 		if (canceled || !url) return;
-		os.api('drive/files/upload-from-url', {
+		misskeyApi('drive/files/upload-from-url', {
 			url: url,
 			folderId: folder.value ? folder.value.id : undefined,
 		});
@@ -326,7 +327,7 @@ function createFolder() {
 		placeholder: i18n.ts.folderName,
 	}).then(({ canceled, result: name }) => {
 		if (canceled) return;
-		os.api('drive/folders/create', {
+		misskeyApi('drive/folders/create', {
 			name: name,
 			parentId: folder.value ? folder.value.id : undefined,
 		}).then(createdFolder => {
@@ -342,7 +343,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) {
 		default: folderToRename.name,
 	}).then(({ canceled, result: name }) => {
 		if (canceled) return;
-		os.api('drive/folders/update', {
+		misskeyApi('drive/folders/update', {
 			folderId: folderToRename.id,
 			name: name,
 		}).then(updatedFolder => {
@@ -353,7 +354,7 @@ function renameFolder(folderToRename: Misskey.entities.DriveFolder) {
 }
 
 function deleteFolder(folderToDelete: Misskey.entities.DriveFolder) {
-	os.api('drive/folders/delete', {
+	misskeyApi('drive/folders/delete', {
 		folderId: folderToDelete.id,
 	}).then(() => {
 		// 削除時に親フォルダに移動
@@ -437,7 +438,7 @@ function move(target?: Misskey.entities.DriveFolder | Misskey.entities.DriveFold
 
 	fetching.value = true;
 
-	os.api('drive/folders/show', {
+	misskeyApi('drive/folders/show', {
 		folderId: target,
 	}).then(folderToMove => {
 		folder.value = folderToMove;
@@ -536,7 +537,7 @@ async function fetch() {
 	const foldersMax = 30;
 	const filesMax = 30;
 
-	const foldersPromise = os.api('drive/folders', {
+	const foldersPromise = misskeyApi('drive/folders', {
 		folderId: folder.value ? folder.value.id : null,
 		limit: foldersMax + 1,
 	}).then(fetchedFolders => {
@@ -547,7 +548,7 @@ async function fetch() {
 		return fetchedFolders;
 	});
 
-	const filesPromise = os.api('drive/files', {
+	const filesPromise = misskeyApi('drive/files', {
 		folderId: folder.value ? folder.value.id : null,
 		type: props.type,
 		limit: filesMax + 1,
@@ -572,7 +573,7 @@ function fetchMoreFolders() {
 
 	const max = 30;
 
-	os.api('drive/folders', {
+	misskeyApi('drive/folders', {
 		folderId: folder.value ? folder.value.id : null,
 		type: props.type,
 		untilId: folders.value.at(-1)?.id,
@@ -595,7 +596,7 @@ function fetchMoreFiles() {
 	const max = 30;
 
 	// ファイル一覧取得
-	os.api('drive/files', {
+	misskeyApi('drive/files', {
 		folderId: folder.value ? folder.value.id : null,
 		type: props.type,
 		untilId: files.value.at(-1)?.id,
diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue
index 6d1bad7433..8a23d7d4bf 100644
--- a/packages/frontend/src/components/MkFeaturedPhotos.vue
+++ b/packages/frontend/src/components/MkFeaturedPhotos.vue
@@ -10,11 +10,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const meta = ref<Misskey.entities.MetaResponse>();
 
-os.api('meta', { detail: true }).then(gotMeta => {
+misskeyApi('meta', { detail: true }).then(gotMeta => {
 	meta.value = gotMeta;
 });
 </script>
diff --git a/packages/frontend/src/components/MkFollowButton.vue b/packages/frontend/src/components/MkFollowButton.vue
index eb5c54de6b..78c4fb3cd2 100644
--- a/packages/frontend/src/components/MkFollowButton.vue
+++ b/packages/frontend/src/components/MkFollowButton.vue
@@ -38,11 +38,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onBeforeUnmount, onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import { claimAchievement } from '@/scripts/achievements.js';
 import { $i } from '@/account.js';
-import { defaultStore } from "@/store.js";
+import { defaultStore } from '@/store.js';
 
 const props = withDefaults(defineProps<{
 	user: Misskey.entities.UserDetailed,
@@ -63,7 +64,7 @@ const wait = ref(false);
 const connection = useStream().useChannel('main');
 
 if (props.user.isFollowing == null) {
-	os.api('users/show', {
+	misskeyApi('users/show', {
 		userId: props.user.id,
 	})
 		.then(onFollowChange);
@@ -88,17 +89,17 @@ async function onClick() {
 
 			if (canceled) return;
 
-			await os.api('following/delete', {
+			await misskeyApi('following/delete', {
 				userId: props.user.id,
 			});
 		} else {
 			if (hasPendingFollowRequestFromYou.value) {
-				await os.api('following/requests/cancel', {
+				await misskeyApi('following/requests/cancel', {
 					userId: props.user.id,
 				});
 				hasPendingFollowRequestFromYou.value = false;
 			} else {
-				await os.api('following/create', {
+				await misskeyApi('following/create', {
 					userId: props.user.id,
 					withReplies: defaultStore.state.defaultWithReplies,
 				});
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index 6f882cfab7..2095a1dcea 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	</template>
 
 	<MkSpacer :marginMin="20" :marginMax="32">
-		<div class="_gaps_m">
+		<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
 			<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
 				<MkInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1">
 					<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
@@ -55,6 +55,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</MkButton>
 			</template>
 		</div>
+		<div v-else class="_fullinfo">
+			<img :src="infoImageUrl" class="_ghost"/>
+			<div>{{ i18n.ts.nothing }}</div>
+		</div>
 	</MkSpacer>
 </MkModalWindow>
 </template>
@@ -70,6 +74,7 @@ import MkButton from './MkButton.vue';
 import MkRadios from './MkRadios.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
+import { infoImageUrl } from '@/instance.js';
 
 const props = defineProps<{
 	title: string;
diff --git a/packages/frontend/src/components/MkHeatmap.vue b/packages/frontend/src/components/MkHeatmap.vue
index a57e6c9292..f47b680f83 100644
--- a/packages/frontend/src/components/MkHeatmap.vue
+++ b/packages/frontend/src/components/MkHeatmap.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { alpha } from '@/scripts/color.js';
@@ -72,19 +72,19 @@ async function renderChart() {
 	let values;
 
 	if (props.src === 'active-users') {
-		const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' });
+		const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
 		values = raw.readWrite;
 	} else if (props.src === 'notes') {
-		const raw = await os.api('charts/notes', { limit: chartLimit, span: 'day' });
+		const raw = await misskeyApi('charts/notes', { limit: chartLimit, span: 'day' });
 		values = raw.local.inc;
 	} else if (props.src === 'ap-requests-inbox-received') {
-		const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' });
+		const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
 		values = raw.inboxReceived;
 	} else if (props.src === 'ap-requests-deliver-succeeded') {
-		const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' });
+		const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
 		values = raw.deliverSucceeded;
 	} else if (props.src === 'ap-requests-deliver-failed') {
-		const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' });
+		const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
 		values = raw.deliverFailed;
 	}
 
diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue
index 8a63e0cced..ac9f673556 100644
--- a/packages/frontend/src/components/MkInstanceCardMini.vue
+++ b/packages/frontend/src/components/MkInstanceCardMini.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
 const props = defineProps<{
@@ -27,7 +27,7 @@ const props = defineProps<{
 
 const chartValues = ref<number[] | null>(null);
 
-os.apiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
+misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, span: 'day' }).then(res => {
 	// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
 	res['requests.received'].splice(0, 1);
 	chartValues.value = res['requests.received'];
diff --git a/packages/frontend/src/components/MkInstanceStats.vue b/packages/frontend/src/components/MkInstanceStats.vue
index 7b763ad385..1576089657 100644
--- a/packages/frontend/src/components/MkInstanceStats.vue
+++ b/packages/frontend/src/components/MkInstanceStats.vue
@@ -90,6 +90,7 @@ import MkSelect from '@/components/MkSelect.vue';
 import MkChart from '@/components/MkChart.vue';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import MkHeatmap from '@/components/MkHeatmap.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
@@ -162,7 +163,7 @@ function createDoughnut(chartEl, tooltip, data) {
 }
 
 onMounted(() => {
-	os.apiGet('federation/stats', { limit: 30 }).then(fedStats => {
+	misskeyApiGet('federation/stats', { limit: 30 }).then(fedStats => {
 		createDoughnut(subDoughnutEl.value, externalTooltipHandler1, fedStats.topSubInstances.map(x => ({
 			name: x.host,
 			color: x.themeColor,
diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue
index 7d4207f0fb..3ec9c3c46a 100644
--- a/packages/frontend/src/components/MkNote.vue
+++ b/packages/frontend/src/components/MkNote.vue
@@ -170,6 +170,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import * as os from '@/os.js';
 import * as sound from '@/scripts/sound.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore, noteViewInterruptors } from '@/store.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
 import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js';
@@ -277,7 +278,7 @@ const keymap = {
 };
 
 provide('react', (reaction: string) => {
-	os.api('notes/reactions/create', {
+	misskeyApi('notes/reactions/create', {
 		noteId: appearNote.value.id,
 		reaction: reaction,
 	});
@@ -298,7 +299,7 @@ if (props.mock) {
 
 if (!props.mock) {
 	useTooltip(renoteButton, async (showing) => {
-		const renotes = await os.api('notes/renotes', {
+		const renotes = await misskeyApi('notes/renotes', {
 			noteId: appearNote.value.id,
 			limit: 11,
 		});
@@ -350,7 +351,7 @@ function react(viaKeyboard = false): void {
 			return;
 		}
 
-		os.api('notes/reactions/create', {
+		misskeyApi('notes/reactions/create', {
 			noteId: appearNote.value.id,
 			reaction: '❤️',
 		});
@@ -371,7 +372,7 @@ function react(viaKeyboard = false): void {
 				return;
 			}
 
-			os.api('notes/reactions/create', {
+			misskeyApi('notes/reactions/create', {
 				noteId: appearNote.value.id,
 				reaction: reaction,
 			});
@@ -393,7 +394,7 @@ function undoReact(note): void {
 		return;
 	}
 
-	os.api('notes/reactions/delete', {
+	misskeyApi('notes/reactions/delete', {
 		noteId: note.id,
 	});
 }
@@ -453,7 +454,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 			icon: 'ti ti-trash',
 			danger: true,
 			action: () => {
-				os.api('notes/delete', {
+				misskeyApi('notes/delete', {
 					noteId: note.value.id,
 				});
 				isDeleted.value = true;
@@ -499,7 +500,7 @@ function focusAfter() {
 }
 
 function readPromo() {
-	os.api('promo/read', {
+	misskeyApi('promo/read', {
 		noteId: appearNote.value.id,
 	});
 	isDeleted.value = true;
diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue
index 33a6786d03..6f0c0323cc 100644
--- a/packages/frontend/src/components/MkNoteDetailed.vue
+++ b/packages/frontend/src/components/MkNoteDetailed.vue
@@ -210,6 +210,7 @@ import { checkWordMute } from '@/scripts/check-word-mute.js';
 import { userPage } from '@/filters/user.js';
 import { notePage } from '@/filters/note.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import * as sound from '@/scripts/sound.js';
 import { defaultStore, noteViewInterruptors } from '@/store.js';
 import { reactionPicker } from '@/scripts/reaction-picker.js';
@@ -292,7 +293,7 @@ const keymap = {
 };
 
 provide('react', (reaction: string) => {
-	os.api('notes/reactions/create', {
+	misskeyApi('notes/reactions/create', {
 		noteId: appearNote.value.id,
 		reaction: reaction,
 	});
@@ -326,7 +327,7 @@ useNoteCapture({
 });
 
 useTooltip(renoteButton, async (showing) => {
-	const renotes = await os.api('notes/renotes', {
+	const renotes = await misskeyApi('notes/renotes', {
 		noteId: appearNote.value.id,
 		limit: 11,
 	});
@@ -371,7 +372,7 @@ function react(viaKeyboard = false): void {
 	if (appearNote.value.reactionAcceptance === 'likeOnly') {
 		sound.play('reaction');
 
-		os.api('notes/reactions/create', {
+		misskeyApi('notes/reactions/create', {
 			noteId: appearNote.value.id,
 			reaction: '❤️',
 		});
@@ -387,7 +388,7 @@ function react(viaKeyboard = false): void {
 		reactionPicker.show(reactButton.value, reaction => {
 			sound.play('reaction');
 
-			os.api('notes/reactions/create', {
+			misskeyApi('notes/reactions/create', {
 				noteId: appearNote.value.id,
 				reaction: reaction,
 			});
@@ -403,7 +404,7 @@ function react(viaKeyboard = false): void {
 function undoReact(note): void {
 	const oldReaction = note.myReaction;
 	if (!oldReaction) return;
-	os.api('notes/reactions/delete', {
+	misskeyApi('notes/reactions/delete', {
 		noteId: note.id,
 	});
 }
@@ -446,7 +447,7 @@ function showRenoteMenu(viaKeyboard = false): void {
 		icon: 'ti ti-trash',
 		danger: true,
 		action: () => {
-			os.api('notes/delete', {
+			misskeyApi('notes/delete', {
 				noteId: note.value.id,
 			});
 			isDeleted.value = true;
@@ -468,7 +469,7 @@ const repliesLoaded = ref(false);
 
 function loadReplies() {
 	repliesLoaded.value = true;
-	os.api('notes/children', {
+	misskeyApi('notes/children', {
 		noteId: appearNote.value.id,
 		limit: 30,
 	}).then(res => {
@@ -480,7 +481,7 @@ const conversationLoaded = ref(false);
 
 function loadConversation() {
 	conversationLoaded.value = true;
-	os.api('notes/conversation', {
+	misskeyApi('notes/conversation', {
 		noteId: appearNote.value.replyId,
 	}).then(res => {
 		conversation.value = res.reverse();
diff --git a/packages/frontend/src/components/MkNoteSub.vue b/packages/frontend/src/components/MkNoteSub.vue
index 40362a955a..c59d568dc2 100644
--- a/packages/frontend/src/components/MkNoteSub.vue
+++ b/packages/frontend/src/components/MkNoteSub.vue
@@ -46,7 +46,7 @@ import MkNoteHeader from '@/components/MkNoteHeader.vue';
 import MkSubNoteContent from '@/components/MkSubNoteContent.vue';
 import MkCwButton from '@/components/MkCwButton.vue';
 import { notePage } from '@/filters/note.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
 import { userPage } from '@/filters/user.js';
@@ -68,7 +68,7 @@ const showContent = ref(false);
 const replies = ref<Misskey.entities.Note[]>([]);
 
 if (props.detail) {
-	os.api('notes/children', {
+	misskeyApi('notes/children', {
 		noteId: props.note.id,
 		limit: 5,
 	}).then(res => {
diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue
index da7eb36d90..ce8b054b39 100644
--- a/packages/frontend/src/components/MkNotification.vue
+++ b/packages/frontend/src/components/MkNotification.vue
@@ -145,7 +145,7 @@ import { getNoteSummary } from '@/scripts/get-note-summary.js';
 import { notePage } from '@/filters/note.js';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i } from '@/account.js';
 import { infoImageUrl } from '@/instance.js';
 
@@ -162,12 +162,12 @@ const followRequestDone = ref(false);
 
 const acceptFollowRequest = () => {
 	followRequestDone.value = true;
-	os.api('following/requests/accept', { userId: props.notification.user.id });
+	misskeyApi('following/requests/accept', { userId: props.notification.user.id });
 };
 
 const rejectFollowRequest = () => {
 	followRequestDone.value = true;
-	os.api('following/requests/reject', { userId: props.notification.user.id });
+	misskeyApi('following/requests/reject', { userId: props.notification.user.id });
 };
 </script>
 
diff --git a/packages/frontend/src/components/MkNumber.vue b/packages/frontend/src/components/MkNumber.vue
index aa04ab253b..1ba4d713b0 100644
--- a/packages/frontend/src/components/MkNumber.vue
+++ b/packages/frontend/src/components/MkNumber.vue
@@ -9,7 +9,6 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { reactive, watch } from 'vue';
-import gsap from 'gsap';
 import number from '@/filters/number.js';
 
 const props = defineProps<{
@@ -20,8 +19,24 @@ const tweened = reactive({
 	number: 0,
 });
 
-watch(() => props.value, (n) => {
-	gsap.to(tweened, { duration: 1, number: Number(n) || 0 });
+watch(() => props.value, (to, from) => {
+	// requestAnimationFrameを利用して、500msでfromからtoまでを1次関数的に変化させる
+	let start: number | null = null;
+
+	function step(timestamp: number) {
+		if (start === null) {
+			start = timestamp;
+		}
+		const elapsed = timestamp - start;
+		tweened.number = (from ?? 0) + (to - (from ?? 0)) * elapsed / 500;
+		if (elapsed < 500) {
+			window.requestAnimationFrame(step);
+		} else {
+			tweened.number = to;
+		}
+	}
+
+	window.requestAnimationFrame(step);
 }, {
 	immediate: true,
 });
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index bdd96238d3..f5b238046a 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -46,6 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ComputedRef, isRef, nextTick, onActivated, onBeforeMount, onBeforeUnmount, onDeactivated, ref, shallowRef, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { onScrollTop, isTopVisible, getBodyScrollHeight, getScrollContainer, onScrollBottom, scrollToBottom, scroll, isBottomVisible } from '@/scripts/scroll.js';
 import { useDocumentVisibility } from '@/scripts/use-document-visibility.js';
 import { defaultStore } from '@/store.js';
@@ -203,7 +204,7 @@ async function init(): Promise<void> {
 	queue.value = new Map();
 	fetching.value = true;
 	const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {};
-	await os.api(props.pagination.endpoint, {
+	await misskeyApi(props.pagination.endpoint, {
 		...params,
 		limit: props.pagination.limit ?? 10,
 		allowPartial: true,
@@ -239,7 +240,7 @@ const fetchMore = async (): Promise<void> => {
 	if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return;
 	moreFetching.value = true;
 	const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {};
-	await os.api(props.pagination.endpoint, {
+	await misskeyApi(props.pagination.endpoint, {
 		...params,
 		limit: SECOND_FETCH_LIMIT,
 		...(props.pagination.offsetMode ? {
@@ -303,7 +304,7 @@ const fetchMoreAhead = async (): Promise<void> => {
 	if (!more.value || fetching.value || moreFetching.value || items.value.size === 0) return;
 	moreFetching.value = true;
 	const params = props.pagination.params ? isRef(props.pagination.params) ? props.pagination.params.value : props.pagination.params : {};
-	await os.api(props.pagination.endpoint, {
+	await misskeyApi(props.pagination.endpoint, {
 		...params,
 		limit: SECOND_FETCH_LIMIT,
 		...(props.pagination.offsetMode ? {
diff --git a/packages/frontend/src/components/MkPasswordDialog.vue b/packages/frontend/src/components/MkPasswordDialog.vue
index c77e912199..7180e5e2ca 100644
--- a/packages/frontend/src/components/MkPasswordDialog.vue
+++ b/packages/frontend/src/components/MkPasswordDialog.vue
@@ -41,7 +41,9 @@ import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
 
 const emit = defineEmits<{
 	(ev: 'done', v: { password: string; token: string | null; }): void;
diff --git a/packages/frontend/src/components/MkPoll.vue b/packages/frontend/src/components/MkPoll.vue
index 682f8e3060..4cac1fe9c3 100644
--- a/packages/frontend/src/components/MkPoll.vue
+++ b/packages/frontend/src/components/MkPoll.vue
@@ -32,11 +32,13 @@ import * as Misskey from 'misskey-js';
 import { sum } from '@/scripts/array.js';
 import { pleaseLogin } from '@/scripts/please-login.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { useInterval } from '@/scripts/use-interval.js';
+import { WithNonNullable } from '@/type.js';
 
 const props = defineProps<{
-	note: Misskey.entities.Note;
+	note: WithNonNullable<Misskey.entities.Note, 'poll'>;
 	readOnly?: boolean;
 }>();
 
@@ -83,7 +85,7 @@ const vote = async (id) => {
 	});
 	if (canceled) return;
 
-	await os.api('notes/polls/vote', {
+	await misskeyApi('notes/polls/vote', {
 		noteId: props.note.id,
 		choice: id,
 	});
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index b86f50eac6..1e073a7de9 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -115,12 +115,13 @@ import { extractMentions } from '@/scripts/extract-mentions.js';
 import { formatTimeString } from '@/scripts/format-time-string.js';
 import { Autocomplete } from '@/scripts/autocomplete.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { selectFiles } from '@/scripts/select-file.js';
 import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js';
 import MkInfo from '@/components/MkInfo.vue';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
-import { $i, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js';
+import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js';
 import { uploadFile } from '@/scripts/upload.js';
 import { deepClone } from '@/scripts/clone.js';
 import MkRippleEffect from '@/components/MkRippleEffect.vue';
@@ -129,6 +130,8 @@ import { claimAchievement } from '@/scripts/achievements.js';
 import { emojiPicker } from '@/scripts/emoji-picker.js';
 import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js';
 
+const $i = signinRequired();
+
 const modal = inject('modal');
 
 const props = withDefaults(defineProps<{
@@ -307,7 +310,7 @@ if (props.reply && props.reply.text != null) {
 	}
 }
 
-if ($i?.isSilenced && visibility.value === 'public') {
+if ($i.isSilenced && visibility.value === 'public') {
 	visibility.value = 'home';
 }
 
@@ -328,7 +331,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
 
 	if (visibility.value === 'specified') {
 		if (props.reply.visibleUserIds) {
-			os.api('users/show', {
+			misskeyApi('users/show', {
 				userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply.userId),
 			}).then(users => {
 				users.forEach(pushVisibleUser);
@@ -336,7 +339,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
 		}
 
 		if (props.reply.userId !== $i.id) {
-			os.api('users/show', { userId: props.reply.userId }).then(user => {
+			misskeyApi('users/show', { userId: props.reply.userId }).then(user => {
 				pushVisibleUser(user);
 			});
 		}
@@ -383,7 +386,7 @@ function addMissingMention() {
 
 	for (const x of extractMentions(ast)) {
 		if (!visibleUsers.value.some(u => (u.username === x.username) && (u.host === x.host))) {
-			os.api('users/show', { username: x.username, host: x.host }).then(user => {
+			misskeyApi('users/show', { username: x.username, host: x.host }).then(user => {
 				visibleUsers.value.push(user);
 			});
 		}
@@ -460,7 +463,7 @@ function setVisibility() {
 
 	os.popup(defineAsyncComponent(() => import('@/components/MkVisibilityPicker.vue')), {
 		currentVisibility: visibility.value,
-		isSilenced: $i?.isSilenced,
+		isSilenced: $i.isSilenced,
 		localOnly: localOnly.value,
 		src: visibilityButton.value,
 	}, {
@@ -784,7 +787,7 @@ async function post(ev?: MouseEvent) {
 	}
 
 	posting.value = true;
-	os.api('notes/create', postData, token).then(() => {
+	misskeyApi('notes/create', postData, token).then(() => {
 		if (props.freezeAfterPosted) {
 			posted.value = true;
 		} else {
diff --git a/packages/frontend/src/components/MkPostFormAttaches.vue b/packages/frontend/src/components/MkPostFormAttaches.vue
index 28a09c571f..31dc48194e 100644
--- a/packages/frontend/src/components/MkPostFormAttaches.vue
+++ b/packages/frontend/src/components/MkPostFormAttaches.vue
@@ -24,6 +24,7 @@ import { defineAsyncComponent, inject } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
@@ -61,7 +62,7 @@ function toggleSensitive(file) {
 		return;
 	}
 
-	os.api('drive/files/update', {
+	misskeyApi('drive/files/update', {
 		fileId: file.id,
 		isSensitive: !file.isSensitive,
 	}).then(() => {
@@ -78,7 +79,7 @@ async function rename(file) {
 		allowEmpty: false,
 	});
 	if (canceled) return;
-	os.api('drive/files/update', {
+	misskeyApi('drive/files/update', {
 		fileId: file.id,
 		name: result,
 	}).then(() => {
@@ -96,7 +97,7 @@ async function describe(file) {
 	}, {
 		done: caption => {
 			let comment = caption.length === 0 ? null : caption;
-			os.api('drive/files/update', {
+			misskeyApi('drive/files/update', {
 				fileId: file.id,
 				comment: comment,
 			}).then(() => {
diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
index ebbd5e6cdc..1b8263ae67 100644
--- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue
+++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue
@@ -45,7 +45,8 @@ import { ref } from 'vue';
 import { $i, getAccounts } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
 import { instance } from '@/instance.js';
-import { api, apiWithDialog, promiseDialog } from '@/os.js';
+import { apiWithDialog, promiseDialog } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 defineProps<{
@@ -82,7 +83,7 @@ function subscribe() {
 			pushSubscription.value = subscription;
 
 			// Register
-			pushRegistrationInServer.value = await api('sw/register', {
+			pushRegistrationInServer.value = await misskeyApi('sw/register', {
 				endpoint: subscription.endpoint,
 				auth: encode(subscription.getKey('auth')),
 				publickey: encode(subscription.getKey('p256dh')),
@@ -159,7 +160,7 @@ if (navigator.serviceWorker == null) {
 			supported.value = true;
 
 			if (pushSubscription.value) {
-				const res = await api('sw/show-registration', {
+				const res = await misskeyApi('sw/show-registration', {
 					endpoint: pushSubscription.value.endpoint,
 				});
 
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index 250b7b96d5..2e75f444da 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -22,6 +22,7 @@ import * as Misskey from 'misskey-js';
 import XDetails from '@/components/MkReactionsViewer.details.vue';
 import MkReactionIcon from '@/components/MkReactionIcon.vue';
 import * as os from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useTooltip } from '@/scripts/use-tooltip.js';
 import { $i } from '@/account.js';
 import MkReactionEffect from '@/components/MkReactionEffect.vue';
@@ -69,11 +70,11 @@ async function toggleReaction() {
 			return;
 		}
 
-		os.api('notes/reactions/delete', {
+		misskeyApi('notes/reactions/delete', {
 			noteId: props.note.id,
 		}).then(() => {
 			if (oldReaction !== props.reaction) {
-				os.api('notes/reactions/create', {
+				misskeyApi('notes/reactions/create', {
 					noteId: props.note.id,
 					reaction: props.reaction,
 				});
@@ -87,7 +88,7 @@ async function toggleReaction() {
 			return;
 		}
 
-		os.api('notes/reactions/create', {
+		misskeyApi('notes/reactions/create', {
 			noteId: props.note.id,
 			reaction: props.reaction,
 		});
@@ -117,7 +118,7 @@ onMounted(() => {
 
 if (!mock) {
 	useTooltip(buttonEl, async (showing) => {
-		const reactions = await os.apiGet('notes/reactions', {
+		const reactions = await misskeyApiGet('notes/reactions', {
 			noteId: props.note.id,
 			type: props.reaction,
 			limit: 10,
diff --git a/packages/frontend/src/components/MkRetentionHeatmap.vue b/packages/frontend/src/components/MkRetentionHeatmap.vue
index e69aa1be80..ef497e0e82 100644
--- a/packages/frontend/src/components/MkRetentionHeatmap.vue
+++ b/packages/frontend/src/components/MkRetentionHeatmap.vue
@@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, nextTick, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { alpha } from '@/scripts/color.js';
@@ -43,7 +43,7 @@ async function renderChart() {
 
 	const maxDays = wide ? 10 : narrow ? 5 : 7;
 
-	let raw = await os.api('retention', { });
+	let raw = await misskeyApi('retention', { });
 
 	raw = raw.slice(0, maxDays + 1);
 
diff --git a/packages/frontend/src/components/MkRetentionLineChart.vue b/packages/frontend/src/components/MkRetentionLineChart.vue
index e2682ec06b..eb05878ae8 100644
--- a/packages/frontend/src/components/MkRetentionLineChart.vue
+++ b/packages/frontend/src/components/MkRetentionLineChart.vue
@@ -16,7 +16,7 @@ import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
 import { alpha } from '@/scripts/color.js';
 import { initChart } from '@/scripts/init-chart.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 initChart();
 
@@ -40,7 +40,7 @@ const getDate = (ymd: string) => {
 };
 
 onMounted(async () => {
-	let raw = await os.api('retention', { });
+	let raw = await misskeyApi('retention', { });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 
diff --git a/packages/frontend/src/components/MkRippleEffect.vue b/packages/frontend/src/components/MkRippleEffect.vue
index 860b083327..11f1aec30f 100644
--- a/packages/frontend/src/components/MkRippleEffect.vue
+++ b/packages/frontend/src/components/MkRippleEffect.vue
@@ -77,7 +77,14 @@ const emit = defineEmits<{
 	(ev: 'end'): void;
 }>();
 
-const particles = [];
+const particles: {
+	size: number;
+	xA: number;
+	yA: number;
+	xB: number;
+	yB: number;
+	color: string;
+}[] = [];
 const origin = 64;
 const colors = ['#FF1493', '#00FFFF', '#FFE202'];
 const zIndex = os.claimZIndex('high');
diff --git a/packages/frontend/src/components/MkSignin.vue b/packages/frontend/src/components/MkSignin.vue
index 2fc2c9ec5e..1c06cff9aa 100644
--- a/packages/frontend/src/components/MkSignin.vue
+++ b/packages/frontend/src/components/MkSignin.vue
@@ -59,6 +59,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import { host as configHost } from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 
@@ -95,7 +96,7 @@ const props = defineProps({
 });
 
 function onUsernameChange(): void {
-	os.api('users/show', {
+	misskeyApi('users/show', {
 		username: username.value,
 	}).then(userResponse => {
 		user.value = userResponse;
@@ -120,7 +121,7 @@ async function queryKey(): Promise<void> {
 			credentialRequest.value = null;
 			queryingKey.value = false;
 			signing.value = true;
-			return os.api('signin', {
+			return misskeyApi('signin', {
 				username: username.value,
 				password: password.value,
 				credential: credential.toJSON(),
@@ -142,7 +143,7 @@ function onSubmit(): void {
 	signing.value = true;
 	if (!totpLogin.value && user.value && user.value.twoFactorEnabled) {
 		if (webAuthnSupported() && user.value.securityKeys) {
-			os.api('signin', {
+			misskeyApi('signin', {
 				username: username.value,
 				password: password.value,
 			}).then(res => {
@@ -159,7 +160,7 @@ function onSubmit(): void {
 			signing.value = false;
 		}
 	} else {
-		os.api('signin', {
+		misskeyApi('signin', {
 			username: username.value,
 			password: password.value,
 			token: user.value?.twoFactorEnabled ? token.value : undefined,
diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue
index 44cfb6f0fa..c71330d62c 100644
--- a/packages/frontend/src/components/MkSignupDialog.form.vue
+++ b/packages/frontend/src/components/MkSignupDialog.form.vue
@@ -84,6 +84,7 @@ import MkInput from './MkInput.vue';
 import MkCaptcha, { type Captcha } from '@/components/MkCaptcha.vue';
 import * as config from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { login } from '@/account.js';
 import { instance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
@@ -180,7 +181,7 @@ function onChangeUsername(): void {
 	usernameState.value = 'wait';
 	usernameAbortController.value = new AbortController();
 
-	os.api('username/available', {
+	misskeyApi('username/available', {
 		username: username.value,
 	}, undefined, usernameAbortController.value.signal).then(result => {
 		usernameState.value = result.available ? 'ok' : 'unavailable';
@@ -203,7 +204,7 @@ function onChangeEmail(): void {
 	emailState.value = 'wait';
 	emailAbortController.value = new AbortController();
 
-	os.api('email-address/available', {
+	misskeyApi('email-address/available', {
 		emailAddress: email.value,
 	}, undefined, emailAbortController.value.signal).then(result => {
 		emailState.value = result.available ? 'ok' :
@@ -245,7 +246,7 @@ async function onSubmit(): Promise<void> {
 	submitting.value = true;
 
 	try {
-		await os.api('signup', {
+		await misskeyApi('signup', {
 			username: username.value,
 			password: password.value,
 			emailAddress: email.value,
@@ -262,7 +263,7 @@ async function onSubmit(): Promise<void> {
 			});
 			emit('signupEmailPending');
 		} else {
-			const res = await os.api('signin', {
+			const res = await misskeyApi('signin', {
 				username: username.value,
 				password: password.value,
 			});
diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue
index 23afb922f3..d5adc02ca7 100644
--- a/packages/frontend/src/components/MkTimeline.vue
+++ b/packages/frontend/src/components/MkTimeline.vue
@@ -11,13 +11,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 		:pagination="paginationQuery"
 		:noGap="!defaultStore.state.showGapBetweenNotesInTimeline"
 		@queue="emit('queue', $event)"
-		@status="prComponent.setDisabled($event)"
+		@status="prComponent?.setDisabled($event)"
 	/>
 </MkPullToRefresh>
 </template>
 
 <script lang="ts" setup>
-import { computed, watch, onUnmounted, provide, ref } from 'vue';
+import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue';
 import { Connection } from 'misskey-js/built/streaming.js';
 import MkNotes from '@/components/MkNotes.vue';
 import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
@@ -62,12 +62,14 @@ type TimelineQueryType = {
   roleId?: string
 }
 
-const prComponent = ref<InstanceType<typeof MkPullToRefresh>>();
-const tlComponent = ref<InstanceType<typeof MkNotes>>();
+const prComponent = shallowRef<InstanceType<typeof MkPullToRefresh>>();
+const tlComponent = shallowRef<InstanceType<typeof MkNotes>>();
 
 let tlNotesCount = 0;
 
-const prepend = note => {
+function prepend(note) {
+	if (tlComponent.value == null) return;
+
 	tlNotesCount++;
 
 	if (instance.notesPerOneAd > 0 && tlNotesCount % instance.notesPerOneAd === 0) {
@@ -81,7 +83,7 @@ const prepend = note => {
 	if (props.sound) {
 		sound.play($i && (note.userId === $i.id) ? 'noteMy' : 'note');
 	}
-};
+}
 
 let connection: Connection;
 let connection2: Connection;
@@ -246,6 +248,8 @@ onUnmounted(() => {
 
 function reloadTimeline() {
 	return new Promise<void>((res) => {
+		if (tlComponent.value == null) return;
+
 		tlNotesCount = 0;
 
 		tlComponent.value.pagingComponent?.reload().then(() => {
diff --git a/packages/frontend/src/components/MkTokenGenerateWindow.vue b/packages/frontend/src/components/MkTokenGenerateWindow.vue
index 8e8e26ed5f..d024e1e593 100644
--- a/packages/frontend/src/components/MkTokenGenerateWindow.vue
+++ b/packages/frontend/src/components/MkTokenGenerateWindow.vue
@@ -11,7 +11,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 	:withOkButton="true"
 	:okButtonDisabled="false"
 	:canClose="false"
-	@close="dialog.close()"
+	@close="dialog?.close()"
 	@closed="$emit('closed')"
 	@ok="ok()"
 >
@@ -87,7 +87,7 @@ function ok(): void {
 		name: name.value,
 		permissions: Object.keys(permissions.value).filter(p => permissions.value[p]),
 	});
-	dialog.value.close();
+	dialog.value?.close();
 }
 
 function disableAll(): void {
diff --git a/packages/frontend/src/components/MkTooltip.vue b/packages/frontend/src/components/MkTooltip.vue
index d21c6317aa..d40cd95f3a 100644
--- a/packages/frontend/src/components/MkTooltip.vue
+++ b/packages/frontend/src/components/MkTooltip.vue
@@ -13,8 +13,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 >
 	<div v-show="showing" ref="el" :class="$style.root" class="_acrylic _shadow" :style="{ zIndex, maxWidth: maxWidth + 'px' }">
 		<slot>
-			<Mfm v-if="asMfm" :text="text"/>
-			<span v-else>{{ text }}</span>
+			<template v-if="text">
+				<Mfm v-if="asMfm" :text="text"/>
+				<span v-else>{{ text }}</span>
+			</template>
 		</slot>
 	</div>
 </Transition>
@@ -53,6 +55,7 @@ const el = shallowRef<HTMLElement>();
 const zIndex = os.claimZIndex('high');
 
 function setPosition() {
+	if (!el.value || !props.targetElement) return;
 	const data = calcPopupPosition(el.value, {
 		anchorElement: props.targetElement,
 		direction: props.direction,
diff --git a/packages/frontend/src/components/MkUpdated.vue b/packages/frontend/src/components/MkUpdated.vue
index 391733931a..29403a84b3 100644
--- a/packages/frontend/src/components/MkUpdated.vue
+++ b/packages/frontend/src/components/MkUpdated.vue
@@ -4,12 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only
 -->
 
 <template>
-<MkModal ref="modal" :zPriority="'middle'" @click="$refs.modal.close()" @closed="$emit('closed')">
+<MkModal ref="modal" :zPriority="'middle'" @click="modal?.close()" @closed="$emit('closed')">
 	<div :class="$style.root">
 		<div :class="$style.title"><MkSparkle>{{ i18n.ts.misskeyUpdated }}</MkSparkle></div>
 		<div :class="$style.version">✨{{ version }}🚀</div>
 		<MkButton full @click="whatIsNew">{{ i18n.ts.whatIsNew }}</MkButton>
-		<MkButton :class="$style.gotIt" primary full @click="$refs.modal.close()">{{ i18n.ts.gotIt }}</MkButton>
+		<MkButton :class="$style.gotIt" primary full @click="modal?.close()">{{ i18n.ts.gotIt }}</MkButton>
 	</div>
 </MkModal>
 </template>
@@ -25,10 +25,10 @@ import { confetti } from '@/scripts/confetti.js';
 
 const modal = shallowRef<InstanceType<typeof MkModal>>();
 
-const whatIsNew = () => {
-	modal.value.close();
+function whatIsNew() {
+	modal.value?.close();
 	window.open(`https://misskey-hub.net/docs/releases/#_${version.replace(/\./g, '')}`, '_blank');
-};
+}
 
 onMounted(() => {
 	confetti({
diff --git a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
index b5489d8e59..af094a8e8c 100644
--- a/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
+++ b/packages/frontend/src/components/MkUserAnnouncementEditDialog.vue
@@ -56,6 +56,7 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -121,7 +122,7 @@ async function del() {
 	});
 	if (canceled) return;
 
-	os.api('admin/announcements/delete', {
+	misskeyApi('admin/announcements/delete', {
 		id: props.announcement.id,
 	}).then(() => {
 		emit('done', {
diff --git a/packages/frontend/src/components/MkUserCardMini.vue b/packages/frontend/src/components/MkUserCardMini.vue
index 75288aac02..9ec5c7b5c7 100644
--- a/packages/frontend/src/components/MkUserCardMini.vue
+++ b/packages/frontend/src/components/MkUserCardMini.vue
@@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import * as Misskey from 'misskey-js';
 import { onMounted, ref } from 'vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { acct } from '@/filters/user.js';
 
 const props = withDefaults(defineProps<{
@@ -32,7 +32,7 @@ const chartValues = ref<number[] | null>(null);
 
 onMounted(() => {
 	if (props.withChart) {
-		os.apiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
+		misskeyApiGet('charts/user/notes', { userId: props.user.id, limit: 16 + 1, span: 'day' }).then(res => {
 			// 今日のぶんの値はまだ途中の値であり、それも含めると大抵の場合前日よりも下降しているようなグラフになってしまうため今日は弾く
 			res.inc.splice(0, 1);
 			chartValues.value = res.inc;
diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue
index df8252fb14..04244ac308 100644
--- a/packages/frontend/src/components/MkUserPopup.vue
+++ b/packages/frontend/src/components/MkUserPopup.vue
@@ -60,6 +60,7 @@ import * as Misskey from 'misskey-js';
 import MkFollowButton from '@/components/MkFollowButton.vue';
 import { userPage } from '@/filters/user.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { getUserMenu } from '@/scripts/get-user-menu.js';
 import number from '@/filters/number.js';
 import { i18n } from '@/i18n.js';
@@ -97,7 +98,7 @@ onMounted(() => {
 			Misskey.acct.parse(props.q.substring(1)) :
 			{ userId: props.q };
 
-		os.api('users/show', query).then(res => {
+		misskeyApi('users/show', query).then(res => {
 			if (!props.showing) return;
 			user.value = res;
 		});
diff --git a/packages/frontend/src/components/MkUserSelectDialog.vue b/packages/frontend/src/components/MkUserSelectDialog.vue
index 9d41147bd2..f4aa06950d 100644
--- a/packages/frontend/src/components/MkUserSelectDialog.vue
+++ b/packages/frontend/src/components/MkUserSelectDialog.vue
@@ -62,7 +62,7 @@ import * as Misskey from 'misskey-js';
 import MkInput from '@/components/MkInput.vue';
 import FormSplit from '@/components/form/split.vue';
 import MkModalWindow from '@/components/MkModalWindow.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
@@ -90,7 +90,7 @@ const search = () => {
 		users.value = [];
 		return;
 	}
-	os.api('users/search-by-username-and-host', {
+	misskeyApi('users/search-by-username-and-host', {
 		username: username.value,
 		host: host.value,
 		limit: 10,
@@ -118,7 +118,7 @@ const cancel = () => {
 };
 
 onMounted(() => {
-	os.api('users/show', {
+	misskeyApi('users/show', {
 		userIds: defaultStore.state.recentlyUsedUsers,
 	}).then(users => {
 		if (props.includeSelf && users.find(x => $i ? x.id === $i.id : true) == null) {
diff --git a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
index ecdfbb4969..c933a14621 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.Privacy.vue
@@ -49,7 +49,7 @@ import { i18n } from '@/i18n.js';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const isLocked = ref(false);
 const hideOnlineStatus = ref(false);
@@ -57,7 +57,7 @@ const noCrawle = ref(false);
 const preventAiLearning = ref(true);
 
 watch([isLocked, hideOnlineStatus, noCrawle, preventAiLearning], () => {
-	os.api('i/update', {
+	misskeyApi('i/update', {
 		isLocked: !!isLocked.value,
 		hideOnlineStatus: !!hideOnlineStatus.value,
 		noCrawle: !!noCrawle.value,
diff --git a/packages/frontend/src/components/MkUserSetupDialog.User.vue b/packages/frontend/src/components/MkUserSetupDialog.User.vue
index 49476c7364..fe9b8930a1 100644
--- a/packages/frontend/src/components/MkUserSetupDialog.User.vue
+++ b/packages/frontend/src/components/MkUserSetupDialog.User.vue
@@ -29,7 +29,7 @@ import * as Misskey from 'misskey-js';
 import { ref } from 'vue';
 import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const props = defineProps<{
 	user: Misskey.entities.UserDetailed;
@@ -39,7 +39,7 @@ const isFollowing = ref(false);
 
 async function follow() {
 	isFollowing.value = true;
-	os.api('following/create', {
+	misskeyApi('following/create', {
 		userId: props.user.id,
 	});
 }
diff --git a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
index 746ed3e0de..e45d594f12 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.ActiveUsersChart.vue
@@ -17,7 +17,7 @@ import { onMounted, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
 import tinycolor from 'tinycolor2';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
@@ -53,7 +53,7 @@ async function renderChart() {
 		}));
 	};
 
-	const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' });
+	const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 
diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue
index 9ed08ee372..ac3d6cabd8 100644
--- a/packages/frontend/src/components/MkVisitorDashboard.vue
+++ b/packages/frontend/src/components/MkVisitorDashboard.vue
@@ -60,6 +60,7 @@ import MkTimeline from '@/components/MkTimeline.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import MkNumber from '@/components/MkNumber.vue';
@@ -68,11 +69,11 @@ import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.
 const meta = ref<Misskey.entities.MetaResponse | null>(null);
 const stats = ref<Misskey.entities.StatsResponse | null>(null);
 
-os.api('meta', { detail: true }).then(_meta => {
+misskeyApi('meta', { detail: true }).then(_meta => {
 	meta.value = _meta;
 });
 
-os.api('stats', {}).then((res) => {
+misskeyApi('stats', {}).then((res) => {
 	stats.value = res;
 });
 
diff --git a/packages/frontend/src/components/MkWindow.vue b/packages/frontend/src/components/MkWindow.vue
index 7c8ffcccf9..f23549efe4 100644
--- a/packages/frontend/src/components/MkWindow.vue
+++ b/packages/frontend/src/components/MkWindow.vue
@@ -143,6 +143,7 @@ function top() {
 }
 
 function maximize() {
+	if (rootEl.value == null) return;
 	maximized.value = true;
 	unResizedTop = rootEl.value.style.top;
 	unResizedLeft = rootEl.value.style.left;
@@ -155,6 +156,7 @@ function maximize() {
 }
 
 function unMaximize() {
+	if (rootEl.value == null) return;
 	maximized.value = false;
 	rootEl.value.style.top = unResizedTop;
 	rootEl.value.style.left = unResizedLeft;
@@ -163,6 +165,7 @@ function unMaximize() {
 }
 
 function minimize() {
+	if (rootEl.value == null) return;
 	minimized.value = true;
 	unResizedWidth = rootEl.value.style.width;
 	unResizedHeight = rootEl.value.style.height;
@@ -171,8 +174,8 @@ function minimize() {
 }
 
 function unMinimize() {
+	if (rootEl.value == null) return;
 	const main = rootEl.value;
-	if (main == null) return;
 
 	minimized.value = false;
 	rootEl.value.style.width = unResizedWidth;
diff --git a/packages/frontend/src/components/page/page.note.vue b/packages/frontend/src/components/page/page.note.vue
index 197b9da53d..5093ee9b79 100644
--- a/packages/frontend/src/components/page/page.note.vue
+++ b/packages/frontend/src/components/page/page.note.vue
@@ -15,7 +15,7 @@ import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkNote from '@/components/MkNote.vue';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const props = defineProps<{
 	block: Misskey.entities.PageBlock,
@@ -26,7 +26,7 @@ const note = ref<Misskey.entities.Note | null>(null);
 
 onMounted(() => {
 	if (props.block.note == null) return;
-	os.api('notes/show', { noteId: props.block.note })
+	misskeyApi('notes/show', { noteId: props.block.note })
 		.then(result => {
 			note.value = result;
 		});
diff --git a/packages/frontend/src/custom-emojis.ts b/packages/frontend/src/custom-emojis.ts
index 6a48159f13..e4ce9cb9cd 100644
--- a/packages/frontend/src/custom-emojis.ts
+++ b/packages/frontend/src/custom-emojis.ts
@@ -5,7 +5,7 @@
 
 import { shallowRef, computed, markRaw, watch } from 'vue';
 import * as Misskey from 'misskey-js';
-import { api, apiGet } from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { get, set } from '@/scripts/idb-proxy.js';
 
@@ -52,11 +52,11 @@ export async function fetchCustomEmojis(force = false) {
 
 	let res;
 	if (force) {
-		res = await api('emojis', {});
+		res = await misskeyApi('emojis', {});
 	} else {
 		const lastFetchedAt = await get('lastEmojisFetchedAt');
 		if (lastFetchedAt && (now - lastFetchedAt) < 1000 * 60 * 60) return;
-		res = await apiGet('emojis', {});
+		res = await misskeyApiGet('emojis', {});
 	}
 
 	customEmojis.value = res.emojis;
diff --git a/packages/frontend/src/filters/bytes.ts b/packages/frontend/src/filters/bytes.ts
index d40b020a9e..2497ddb775 100644
--- a/packages/frontend/src/filters/bytes.ts
+++ b/packages/frontend/src/filters/bytes.ts
@@ -5,10 +5,10 @@
 
 export default (v, digits = 0) => {
 	if (v == null) return '?';
-	const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
+	const sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'RB', 'QB'];
 	if (v === 0) return '0';
 	const isMinus = v < 0;
 	if (isMinus) v = -v;
 	const i = Math.floor(Math.log(v) / Math.log(1024));
-	return (isMinus ? '-' : '') + (v / Math.pow(1024, i)).toFixed(digits).replace(/\.0+$/, '') + sizes[i];
+	return (isMinus ? '-' : '') + (v / Math.pow(1024, i)).toFixed(digits).replace(/(\.[1-9]*)0+$/, '$1').replace(/\.$/, '') + (sizes[i] ?? `e+${ i * 3 }B`);
 };
diff --git a/packages/frontend/src/filters/kmg.ts b/packages/frontend/src/filters/kmg.ts
new file mode 100644
index 0000000000..4dcb5c5800
--- /dev/null
+++ b/packages/frontend/src/filters/kmg.ts
@@ -0,0 +1,9 @@
+export default (v, fractionDigits = 0) => {
+	if (v == null) return 'N/A';
+	if (v === 0) return '0';
+	const sizes = ['', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y', 'R', 'Q'];
+	const isMinus = v < 0;
+	if (isMinus) v = -v;
+	const i = Math.floor(Math.log(v) / Math.log(1000));
+	return (isMinus ? '-' : '') + (v / Math.pow(1000, i)).toFixed(fractionDigits).replace(/(\.[1-9]*)0+$/, '$1').replace(/\.$/, '') + (sizes[i] ?? `e+${ i * 3 }`);
+};
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index b09264dabb..739e90101b 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -5,7 +5,7 @@
 
 import { computed, reactive } from 'vue';
 import * as Misskey from 'misskey-js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const.js';
 
@@ -26,7 +26,7 @@ export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO
 export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
 
 export async function fetchInstance() {
-	const meta = await api('meta', {
+	const meta = await misskeyApi('meta', {
 		detail: false,
 	});
 
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index b02f6aa640..a63d61bb8f 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -5,12 +5,11 @@
 
 // TODO: なんでもかんでもos.tsに突っ込むのやめたいのでよしなに分割する
 
-import { pendingApiRequestsCount, api, apiGet } from '@/scripts/api.js';
-export { pendingApiRequestsCount, api, apiGet };
 import { Component, markRaw, Ref, ref, defineAsyncComponent } from 'vue';
 import { EventEmitter } from 'eventemitter3';
 import insertTextAtCursor from 'insert-text-at-cursor';
 import * as Misskey from 'misskey-js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import MkPostFormDialog from '@/components/MkPostFormDialog.vue';
 import MkWaitingDialog from '@/components/MkWaitingDialog.vue';
@@ -33,7 +32,7 @@ export const apiWithDialog = ((
 	data: Record<string, any> = {},
 	token?: string | null | undefined,
 ) => {
-	const promise = api(endpoint, data, token);
+	const promise = misskeyApi(endpoint, data, token);
 	promiseDialog(promise, null, async (err) => {
 		let title = null;
 		let text = err.message + '\n' + (err as any).id;
@@ -83,7 +82,7 @@ export const apiWithDialog = ((
 	});
 
 	return promise;
-}) as typeof api;
+}) as typeof misskeyApi;
 
 export function promiseDialog<T extends Promise<any>>(
 	promise: T,
@@ -621,7 +620,7 @@ export function checkExistence(fileData: ArrayBuffer): Promise<any> {
 		const data = new FormData();
 		data.append('md5', getMD5(fileData));
 
-		os.api('drive/files/find-by-hash', {
+		api('drive/files/find-by-hash', {
 			md5: getMD5(fileData)
 		}).then(resp => {
 			resolve(resp.length > 0 ? resp[0] : null);
diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue
index 72a12e3c7b..10e8ccda06 100644
--- a/packages/frontend/src/pages/_error_.vue
+++ b/packages/frontend/src/pages/_error_.vue
@@ -29,7 +29,7 @@ import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import { version } from '@/config.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -46,7 +46,7 @@ const loaded = ref(false);
 const serverIsDead = ref(false);
 const meta = ref<Misskey.entities.MetaResponse | null>(null);
 
-os.api('meta', {
+misskeyApi('meta', {
 	detail: false,
 }).then(res => {
 	loaded.value = true;
diff --git a/packages/frontend/src/pages/about.vue b/packages/frontend/src/pages/about.vue
index 44095348f6..f402b26ad8 100644
--- a/packages/frontend/src/pages/about.vue
+++ b/packages/frontend/src/pages/about.vue
@@ -114,7 +114,7 @@ import FormSplit from '@/components/form/split.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkInstanceStats from '@/components/MkInstanceStats.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import number from '@/filters/number.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -136,7 +136,7 @@ watch(tab, () => {
 	}
 });
 
-const initStats = () => os.api('stats', {
+const initStats = () => misskeyApi('stats', {
 }).then((res) => {
 	stats.value = res;
 });
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 56b5e7d926..4a9c659a97 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -79,6 +79,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import bytes from '@/filters/bytes.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { iAmAdmin, iAmModerator } from '@/account.js';
@@ -93,8 +94,8 @@ const props = defineProps<{
 }>();
 
 async function fetch() {
-	file.value = await os.api('drive/files/show', { fileId: props.fileId });
-	info.value = await os.api('admin/drive/show-file', { fileId: props.fileId });
+	file.value = await misskeyApi('drive/files/show', { fileId: props.fileId });
+	info.value = await misskeyApi('admin/drive/show-file', { fileId: props.fileId });
 	isSensitive.value = file.value.isSensitive;
 }
 
@@ -113,7 +114,7 @@ async function del() {
 }
 
 async function toggleIsSensitive(v) {
-	await os.api('drive/files/update', { fileId: props.fileId, isSensitive: v });
+	await misskeyApi('drive/files/update', { fileId: props.fileId, isSensitive: v });
 	isSensitive.value = v;
 }
 
diff --git a/packages/frontend/src/pages/admin-user.vue b/packages/frontend/src/pages/admin-user.vue
index d69d627ce8..85417f0ecb 100644
--- a/packages/frontend/src/pages/admin-user.vue
+++ b/packages/frontend/src/pages/admin-user.vue
@@ -219,11 +219,12 @@ import FormSuspense from '@/components/form/suspense.vue';
 import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { url } from '@/config.js';
 import { acct } from '@/filters/user.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
-import { iAmAdmin, $i } from '@/account.js';
+import { iAmAdmin, $i, iAmModerator } from '@/account.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 import MkPagination from '@/components/MkPagination.vue';
 
@@ -262,11 +263,11 @@ const announcementsPagination = {
 const expandedRoles = ref([]);
 
 function createFetcher() {
-	return () => Promise.all([os.api('users/show', {
+	return () => Promise.all([misskeyApi('users/show', {
 		userId: props.userId,
-	}), os.api('admin/show-user', {
+	}), misskeyApi('admin/show-user', {
 		userId: props.userId,
-	}), iAmAdmin ? os.api('admin/get-user-ips', {
+	}), iAmAdmin ? misskeyApi('admin/get-user-ips', {
 		userId: props.userId,
 	}) : Promise.resolve(null)]).then(([_user, _info, _ips]) => {
 		user.value = _user;
@@ -278,7 +279,7 @@ function createFetcher() {
 		moderationNote.value = info.value.moderationNote;
 
 		watch(moderationNote, async () => {
-			await os.api('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
+			await misskeyApi('admin/update-user-note', { userId: user.value.id, text: moderationNote.value });
 			await refreshUser();
 		});
 	});
@@ -301,7 +302,7 @@ async function resetPassword() {
 	if (confirm.canceled) {
 		return;
 	} else {
-		const { password } = await os.api('admin/reset-password', {
+		const { password } = await misskeyApi('admin/reset-password', {
 			userId: user.value.id,
 		});
 		os.alert({
@@ -319,7 +320,7 @@ async function toggleSuspend(v) {
 	if (confirm.canceled) {
 		suspended.value = !v;
 	} else {
-		await os.api(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id });
+		await misskeyApi(v ? 'admin/suspend-user' : 'admin/unsuspend-user', { userId: user.value.id });
 		await refreshUser();
 	}
 }
@@ -331,7 +332,7 @@ async function unsetUserAvatar() {
 	});
 	if (confirm.canceled) return;
 	const process = async () => {
-		await os.api('admin/unset-user-avatar', { userId: user.value.id });
+		await misskeyApi('admin/unset-user-avatar', { userId: user.value.id });
 		os.success();
 	};
 	await process().catch(err => {
@@ -350,7 +351,7 @@ async function unsetUserBanner() {
 	});
 	if (confirm.canceled) return;
 	const process = async () => {
-		await os.api('admin/unset-user-banner', { userId: user.value.id });
+		await misskeyApi('admin/unset-user-banner', { userId: user.value.id });
 		os.success();
 	};
 	await process().catch(err => {
@@ -369,7 +370,7 @@ async function deleteAllFiles() {
 	});
 	if (confirm.canceled) return;
 	const process = async () => {
-		await os.api('admin/delete-all-files-of-a-user', { userId: user.value.id });
+		await misskeyApi('admin/delete-all-files-of-a-user', { userId: user.value.id });
 		os.success();
 	};
 	await process().catch(err => {
@@ -406,7 +407,7 @@ async function deleteAccount() {
 }
 
 async function assignRole() {
-	const roles = await os.api('admin/roles/list');
+	const roles = await misskeyApi('admin/roles/list');
 
 	const { canceled, result: roleId } = await os.select({
 		title: i18n.ts._role.chooseRoleToAssign,
@@ -482,7 +483,7 @@ watch(() => props.userId, () => {
 });
 
 watch(user, () => {
-	os.api('ap/get', {
+	misskeyApi('ap/get', {
 		uri: user.value.uri ?? `${url}/users/${user.value.id}`,
 	}).then(res => {
 		ap.value = res;
diff --git a/packages/frontend/src/pages/admin/ads.vue b/packages/frontend/src/pages/admin/ads.vue
index 5884ac74b5..eb9aef0e48 100644
--- a/packages/frontend/src/pages/admin/ads.vue
+++ b/packages/frontend/src/pages/admin/ads.vue
@@ -96,6 +96,7 @@ import MkFolder from '@/components/MkFolder.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -108,7 +109,7 @@ const daysOfWeek: string[] = [i18n.ts._weekday.sunday, i18n.ts._weekday.monday,
 const filterType = ref('all');
 let publishing: boolean | null = null;
 
-os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
+misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
 	if (adsResponse != null) {
 		ads.value = adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
@@ -174,7 +175,7 @@ function remove(ad) {
 
 function save(ad) {
 	if (ad.id == null) {
-		os.api('admin/ad/create', {
+		misskeyApi('admin/ad/create', {
 			...ad,
 			expiresAt: new Date(ad.expiresAt).getTime(),
 			startsAt: new Date(ad.startsAt).getTime(),
@@ -191,7 +192,7 @@ function save(ad) {
 			});
 		});
 	} else {
-		os.api('admin/ad/update', {
+		misskeyApi('admin/ad/update', {
 			...ad,
 			expiresAt: new Date(ad.expiresAt).getTime(),
 			startsAt: new Date(ad.startsAt).getTime(),
@@ -210,7 +211,7 @@ function save(ad) {
 }
 
 function more() {
-	os.api('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
+	misskeyApi('admin/ad/list', { untilId: ads.value.reduce((acc, ad) => ad.id != null ? ad : acc).id, publishing: publishing }).then(adsResponse => {
 		if (adsResponse == null) return;
 		ads.value = ads.value.concat(adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
@@ -227,7 +228,7 @@ function more() {
 }
 
 function refresh() {
-	os.api('admin/ad/list', { publishing: publishing }).then(adsResponse => {
+	misskeyApi('admin/ad/list', { publishing: publishing }).then(adsResponse => {
 		if (adsResponse == null) return;
 		ads.value = adsResponse.map(r => {
 			const exdate = new Date(r.expiresAt);
diff --git a/packages/frontend/src/pages/admin/announcements.vue b/packages/frontend/src/pages/admin/announcements.vue
index e4bbe15955..f941d512b3 100644
--- a/packages/frontend/src/pages/admin/announcements.vue
+++ b/packages/frontend/src/pages/admin/announcements.vue
@@ -79,6 +79,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkRadios from '@/components/MkRadios.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
@@ -86,7 +87,7 @@ import MkTextarea from '@/components/MkTextarea.vue';
 
 const announcements = ref<any[]>([]);
 
-os.api('admin/announcements/list').then(announcementResponse => {
+misskeyApi('admin/announcements/list').then(announcementResponse => {
 	announcements.value = announcementResponse;
 });
 
@@ -112,7 +113,7 @@ function del(announcement) {
 	}).then(({ canceled }) => {
 		if (canceled) return;
 		announcements.value = announcements.value.filter(x => x !== announcement);
-		os.api('admin/announcements/delete', announcement);
+		misskeyApi('admin/announcements/delete', announcement);
 	});
 }
 
@@ -134,13 +135,13 @@ async function save(announcement) {
 }
 
 function more() {
-	os.api('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
+	misskeyApi('admin/announcements/list', { untilId: announcements.value.reduce((acc, announcement) => announcement.id != null ? announcement : acc).id }).then(announcementResponse => {
 		announcements.value = announcements.value.concat(announcementResponse);
 	});
 }
 
 function refresh() {
-	os.api('admin/announcements/list').then(announcementResponse => {
+	misskeyApi('admin/announcements/list').then(announcementResponse => {
 		announcements.value = announcementResponse;
 	});
 }
diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue
index 8c9d670d11..99b8070b71 100644
--- a/packages/frontend/src/pages/admin/bot-protection.vue
+++ b/packages/frontend/src/pages/admin/bot-protection.vue
@@ -72,6 +72,7 @@ import MkButton from '@/components/MkButton.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSlot from '@/components/form/slot.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 
@@ -86,7 +87,7 @@ const turnstileSiteKey = ref<string | null>(null);
 const turnstileSecretKey = ref<string | null>(null);
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	hcaptchaSiteKey.value = meta.hcaptchaSiteKey;
 	hcaptchaSecretKey.value = meta.hcaptchaSecretKey;
 	recaptchaSiteKey.value = meta.recaptchaSiteKey;
diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue
index 38cce69735..72b47949e7 100644
--- a/packages/frontend/src/pages/admin/branding.vue
+++ b/packages/frontend/src/pages/admin/branding.vue
@@ -101,6 +101,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { instance, fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -122,7 +123,7 @@ const notFoundImageUrl = ref<string | null>(null);
 const manifestJsonOverride = ref<string>('{}');
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	iconUrl.value = meta.iconUrl;
 	app192IconUrl.value = meta.app192IconUrl;
 	app512IconUrl.value = meta.app512IconUrl;
diff --git a/packages/frontend/src/pages/admin/database.vue b/packages/frontend/src/pages/admin/database.vue
index 53f556bb64..27541f576d 100644
--- a/packages/frontend/src/pages/admin/database.vue
+++ b/packages/frontend/src/pages/admin/database.vue
@@ -21,13 +21,13 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import bytes from '@/filters/bytes.js';
 import number from '@/filters/number.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
-const databasePromiseFactory = () => os.api('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
+const databasePromiseFactory = () => misskeyApi('admin/get-table-stats').then(res => Object.entries(res).sort((a, b) => b[1].size - a[1].size));
 
 const headerActions = computed(() => []);
 
diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue
index 2e6ad3b1d3..c6fd02d67e 100644
--- a/packages/frontend/src/pages/admin/email-settings.vue
+++ b/packages/frontend/src/pages/admin/email-settings.vue
@@ -73,6 +73,7 @@ import FormSuspense from '@/components/form/suspense.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSection from '@/components/form/section.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance, instance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -87,7 +88,7 @@ const smtpUser = ref<string>('');
 const smtpPass = ref<string>('');
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	enableEmail.value = meta.enableEmail;
 	email.value = meta.email;
 	smtpSecure.value = meta.smtpSecure;
diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue
index 22dc115fda..4468c8279f 100644
--- a/packages/frontend/src/pages/admin/external-services.vue
+++ b/packages/frontend/src/pages/admin/external-services.vue
@@ -42,6 +42,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSection from '@/components/form/section.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -50,7 +51,7 @@ const deeplAuthKey = ref<string>('');
 const deeplIsPro = ref<boolean>(false);
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	deeplAuthKey.value = meta.deeplAuthKey;
 	deeplIsPro.value = meta.deeplIsPro;
 }
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index a366b302c7..a614cacd45 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -42,6 +42,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -83,7 +84,7 @@ async function find() {
 	});
 	if (canceled) return;
 
-	os.api('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
+	misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
 		show(file);
 	}).catch(err => {
 		if (err.code === 'NO_SUCH_FILE') {
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 5e92cbd600..333bac724b 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -34,6 +34,7 @@ import MkSuperMenu from '@/components/MkSuperMenu.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import { instance } from '@/instance.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
 import { useRouter } from '@/router.js';
 import { PageMetadata, definePageMetadata, provideMetadataReceiver } from '@/scripts/page-metadata.js';
@@ -62,7 +63,7 @@ let noEmailServer = !instance.enableEmail;
 const thereIsUnresolvedAbuseReport = ref(false);
 const currentPage = computed(() => router.currentRef.value.child);
 
-os.api('admin/abuse-user-reports', {
+misskeyApi('admin/abuse-user-reports', {
 	state: 'unresolved',
 	limit: 1,
 }).then(reports => {
@@ -266,7 +267,7 @@ provideMetadataReceiver((info) => {
 });
 
 function invite() {
-	os.api('admin/invite/create').then(x => {
+	misskeyApi('admin/invite/create').then(x => {
 		os.alert({
 			type: 'info',
 			text: x[0].code,
diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue
index 356eca2af6..58e147f111 100644
--- a/packages/frontend/src/pages/admin/instance-block.vue
+++ b/packages/frontend/src/pages/admin/instance-block.vue
@@ -29,6 +29,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -38,7 +39,7 @@ const silencedHosts = ref<string>('');
 const tab = ref('block');
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	blockedHosts.value = meta.blockedHosts.join('\n');
 	silencedHosts.value = meta.silencedHosts.join('\n');
 }
diff --git a/packages/frontend/src/pages/admin/invites.vue b/packages/frontend/src/pages/admin/invites.vue
index 838ef52b14..8b06ec59bd 100644
--- a/packages/frontend/src/pages/admin/invites.vue
+++ b/packages/frontend/src/pages/admin/invites.vue
@@ -59,6 +59,7 @@ import { computed, ref, shallowRef } from 'vue';
 import XHeader from './_header_.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSelect from '@/components/MkSelect.vue';
@@ -93,14 +94,14 @@ async function createWithOptions() {
 		count: createCount.value,
 	};
 
-	const tickets = await os.api('admin/invite/create', options);
+	const tickets = await misskeyApi('admin/invite/create', options);
 	os.alert({
 		type: 'success',
 		title: i18n.ts.inviteCodeCreated,
-		text: tickets?.map(x => x.code).join('\n'),
+		text: tickets.map(x => x.code).join('\n'),
 	});
 
-	tickets?.forEach(ticket => pagingComponent.value?.prepend(ticket));
+	tickets.forEach(ticket => pagingComponent.value?.prepend(ticket));
 }
 
 function deleted(id: string) {
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index f6c0b29403..4915bee713 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -66,6 +66,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -81,7 +82,7 @@ const tosUrl = ref<string | null>(null);
 const privacyPolicyUrl = ref<string | null>(null);
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	enableRegistration.value = !meta.disableRegistration;
 	emailRequiredForSignup.value = meta.emailRequiredForSignup;
 	sensitiveWords.value = meta.sensitiveWords.join('\n');
diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue
index 7019971e90..03b29f1873 100644
--- a/packages/frontend/src/pages/admin/object-storage.vue
+++ b/packages/frontend/src/pages/admin/object-storage.vue
@@ -90,6 +90,7 @@ import MkInput from '@/components/MkInput.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -110,7 +111,7 @@ const objectStorageSetPublicRead = ref<boolean>(false);
 const objectStorageS3ForcePathStyle = ref<boolean>(true);
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	useObjectStorage.value = meta.useObjectStorage;
 	objectStorageBaseUrl.value = meta.objectStorageBaseUrl;
 	objectStorageBucket.value = meta.objectStorageBucket;
diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue
index 5bb328ac92..bb0199eb40 100644
--- a/packages/frontend/src/pages/admin/other-settings.vue
+++ b/packages/frontend/src/pages/admin/other-settings.vue
@@ -47,6 +47,7 @@ import { ref, computed } from 'vue';
 import XHeader from './_header_.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -58,7 +59,7 @@ const enableChartsForRemoteUser = ref<boolean>(false);
 const enableChartsForFederatedInstances = ref<boolean>(false);
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	enableServerMachineStats.value = meta.enableServerMachineStats;
 	enableIdenticonGeneration.value = meta.enableIdenticonGeneration;
 	enableChartsForRemoteUser.value = meta.enableChartsForRemoteUser;
diff --git a/packages/frontend/src/pages/admin/overview.active-users.vue b/packages/frontend/src/pages/admin/overview.active-users.vue
index 5e67370c2b..8acc5786b2 100644
--- a/packages/frontend/src/pages/admin/overview.active-users.vue
+++ b/packages/frontend/src/pages/admin/overview.active-users.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
@@ -52,7 +52,7 @@ async function renderChart() {
 		}));
 	};
 
-	const raw = await os.api('charts/active-users', { limit: chartLimit, span: 'day' });
+	const raw = await misskeyApi('charts/active-users', { limit: chartLimit, span: 'day' });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 
diff --git a/packages/frontend/src/pages/admin/overview.ap-requests.vue b/packages/frontend/src/pages/admin/overview.ap-requests.vue
index 0de62fadea..694be9a590 100644
--- a/packages/frontend/src/pages/admin/overview.ap-requests.vue
+++ b/packages/frontend/src/pages/admin/overview.ap-requests.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
 import { defaultStore } from '@/store.js';
@@ -65,7 +65,7 @@ onMounted(async () => {
 		}));
 	};
 
-	const raw = await os.api('charts/ap-request', { limit: chartLimit, span: 'day' });
+	const raw = await misskeyApi('charts/ap-request', { limit: chartLimit, span: 'day' });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 	const succColor = '#87e000';
diff --git a/packages/frontend/src/pages/admin/overview.federation.vue b/packages/frontend/src/pages/admin/overview.federation.vue
index cfd1c6a566..d3503bd462 100644
--- a/packages/frontend/src/pages/admin/overview.federation.vue
+++ b/packages/frontend/src/pages/admin/overview.federation.vue
@@ -49,6 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, ref } from 'vue';
 import XPie, { type InstanceForPie } from './overview.pie.vue';
 import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import number from '@/filters/number.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import { i18n } from '@/i18n.js';
@@ -65,13 +66,13 @@ const fetching = ref(true);
 const { handler: externalTooltipHandler } = useChartTooltip();
 
 onMounted(async () => {
-	const chart = await os.apiGet('charts/federation', { limit: 2, span: 'day' });
+	const chart = await misskeyApiGet('charts/federation', { limit: 2, span: 'day' });
 	federationPubActive.value = chart.pubActive[0];
 	federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
 	federationSubActive.value = chart.subActive[0];
 	federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
 
-	os.apiGet('federation/stats', { limit: 10 }).then(res => {
+	misskeyApiGet('federation/stats', { limit: 10 }).then(res => {
 		topSubInstancesForPie.value = [
 			...res.topSubInstances.map(x => ({
 				name: x.host,
diff --git a/packages/frontend/src/pages/admin/overview.instances.vue b/packages/frontend/src/pages/admin/overview.instances.vue
index de34f0c09b..8d731cbc90 100644
--- a/packages/frontend/src/pages/admin/overview.instances.vue
+++ b/packages/frontend/src/pages/admin/overview.instances.vue
@@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import MkInstanceCardMini from '@/components/MkInstanceCardMini.vue';
 import { defaultStore } from '@/store.js';
@@ -28,7 +28,7 @@ const instances = ref<Misskey.entities.FederationInstance[]>([]);
 const fetching = ref(true);
 
 const fetch = async () => {
-	const fetchedInstances = await os.api('federation/instances', {
+	const fetchedInstances = await misskeyApi('federation/instances', {
 		sort: '+latestRequestReceivedAt',
 		limit: 6,
 	});
diff --git a/packages/frontend/src/pages/admin/overview.moderators.vue b/packages/frontend/src/pages/admin/overview.moderators.vue
index 3034bdd57e..75b731996b 100644
--- a/packages/frontend/src/pages/admin/overview.moderators.vue
+++ b/packages/frontend/src/pages/admin/overview.moderators.vue
@@ -18,15 +18,15 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
 import { defaultStore } from '@/store.js';
 
 const moderators = ref<Misskey.entities.UserDetailed[] | null>(null);
 const fetching = ref(true);
 
 onMounted(async () => {
-	moderators.value = await os.api('admin/show-users', {
+	moderators.value = await misskeyApi('admin/show-users', {
 		sort: '+lastActiveDate',
 		state: 'adminOrModerator',
 		limit: 30,
diff --git a/packages/frontend/src/pages/admin/overview.stats.vue b/packages/frontend/src/pages/admin/overview.stats.vue
index 78f435e731..3fb24747cd 100644
--- a/packages/frontend/src/pages/admin/overview.stats.vue
+++ b/packages/frontend/src/pages/admin/overview.stats.vue
@@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import MkNumberDiff from '@/components/MkNumberDiff.vue';
 import MkNumber from '@/components/MkNumber.vue';
 import { i18n } from '@/i18n.js';
@@ -78,17 +78,17 @@ const fetching = ref(true);
 
 onMounted(async () => {
 	const [_stats, _onlineUsersCount] = await Promise.all([
-		os.api('stats', {}),
-		os.apiGet('get-online-users-count').then(res => res.count),
+		misskeyApi('stats', {}),
+		misskeyApiGet('get-online-users-count').then(res => res.count),
 	]);
 	stats.value = _stats;
 	onlineUsersCount.value = _onlineUsersCount;
 
-	os.apiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
+	misskeyApiGet('charts/users', { limit: 2, span: 'day' }).then(chart => {
 		usersComparedToThePrevDay.value = stats.value.originalUsersCount - chart.local.total[1];
 	});
 
-	os.apiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
+	misskeyApiGet('charts/notes', { limit: 2, span: 'day' }).then(chart => {
 		notesComparedToThePrevDay.value = stats.value.originalNotesCount - chart.local.total[1];
 	});
 
diff --git a/packages/frontend/src/pages/admin/overview.users.vue b/packages/frontend/src/pages/admin/overview.users.vue
index 79579367c1..530ca0933e 100644
--- a/packages/frontend/src/pages/admin/overview.users.vue
+++ b/packages/frontend/src/pages/admin/overview.users.vue
@@ -18,8 +18,8 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { ref } from 'vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import { defaultStore } from '@/store.js';
@@ -28,7 +28,7 @@ const newUsers = ref<Misskey.entities.UserDetailed[] | null>(null);
 const fetching = ref(true);
 
 const fetch = async () => {
-	const _newUsers = await os.api('admin/show-users', {
+	const _newUsers = await misskeyApi('admin/show-users', {
 		limit: 5,
 		sort: '+createdAt',
 		origin: 'local',
diff --git a/packages/frontend/src/pages/admin/overview.vue b/packages/frontend/src/pages/admin/overview.vue
index 2e0791e24f..df058f6952 100644
--- a/packages/frontend/src/pages/admin/overview.vue
+++ b/packages/frontend/src/pages/admin/overview.vue
@@ -79,6 +79,7 @@ import XModerators from './overview.moderators.vue';
 import XHeatmap from './overview.heatmap.vue';
 import type { InstanceForPie } from './overview.pie.vue';
 import * as os from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -117,14 +118,14 @@ onMounted(async () => {
 	magicGrid.listen();
 	*/
 
-	os.apiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => {
+	misskeyApiGet('charts/federation', { limit: 2, span: 'day' }).then(chart => {
 		federationPubActive.value = chart.pubActive[0];
 		federationPubActiveDiff.value = chart.pubActive[0] - chart.pubActive[1];
 		federationSubActive.value = chart.subActive[0];
 		federationSubActiveDiff.value = chart.subActive[0] - chart.subActive[1];
 	});
 
-	os.apiGet('federation/stats', { limit: 10 }).then(res => {
+	misskeyApiGet('federation/stats', { limit: 10 }).then(res => {
 		topSubInstancesForPie.value = [
 			...res.topSubInstances.map(x => ({
 				name: x.host,
@@ -149,18 +150,18 @@ onMounted(async () => {
 		];
 	});
 
-	os.api('admin/server-info').then(serverInfoResponse => {
+	misskeyApi('admin/server-info').then(serverInfoResponse => {
 		serverInfo.value = serverInfoResponse;
 	});
 
-	os.api('admin/show-users', {
+	misskeyApi('admin/show-users', {
 		limit: 5,
 		sort: '+createdAt',
 	}).then(res => {
 		newUsers.value = res;
 	});
 
-	os.api('federation/instances', {
+	misskeyApi('federation/instances', {
 		sort: '+latestRequestReceivedAt',
 		limit: 25,
 	}).then(res => {
diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue
index 05e48f7ac1..8b1b3280f3 100644
--- a/packages/frontend/src/pages/admin/proxy-account.vue
+++ b/packages/frontend/src/pages/admin/proxy-account.vue
@@ -28,6 +28,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -36,10 +37,10 @@ const proxyAccount = ref<Misskey.entities.UserDetailed | null>(null);
 const proxyAccountId = ref<string | null>(null);
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	proxyAccountId.value = meta.proxyAccountId;
 	if (proxyAccountId.value) {
-		proxyAccount.value = await os.api('users/show', { userId: proxyAccountId.value });
+		proxyAccount.value = await misskeyApi('users/show', { userId: proxyAccountId.value });
 	}
 }
 
diff --git a/packages/frontend/src/pages/admin/queue.chart.vue b/packages/frontend/src/pages/admin/queue.chart.vue
index 72276c1eed..085e5dbb7a 100644
--- a/packages/frontend/src/pages/admin/queue.chart.vue
+++ b/packages/frontend/src/pages/admin/queue.chart.vue
@@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { markRaw, onMounted, onUnmounted, ref, shallowRef } from 'vue';
 import XChart from './queue.chart.chart.vue';
 import number from '@/filters/number.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import MkFolder from '@/components/MkFolder.vue';
@@ -105,7 +105,7 @@ const onStatsLog = (statsLog) => {
 
 onMounted(() => {
 	if (props.domain === 'inbox' || props.domain === 'deliver') {
-		os.api(`admin/queue/${props.domain}-delayed`).then(result => {
+		misskeyApi(`admin/queue/${props.domain}-delayed`).then(result => {
 			jobs.value = result;
 		});
 	}
diff --git a/packages/frontend/src/pages/admin/relays.vue b/packages/frontend/src/pages/admin/relays.vue
index 0056f2bd9f..6811a8eba5 100644
--- a/packages/frontend/src/pages/admin/relays.vue
+++ b/packages/frontend/src/pages/admin/relays.vue
@@ -29,6 +29,7 @@ import * as Misskey from 'misskey-js';
 import XHeader from './_header_.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -41,7 +42,7 @@ async function addRelay() {
 		placeholder: i18n.ts.inboxUrl,
 	});
 	if (canceled) return;
-	os.api('admin/relays/add', {
+	misskeyApi('admin/relays/add', {
 		inbox,
 	}).then((relay: any) => {
 		refresh();
@@ -54,7 +55,7 @@ async function addRelay() {
 }
 
 function remove(inbox: string) {
-	os.api('admin/relays/remove', {
+	misskeyApi('admin/relays/remove', {
 		inbox,
 	}).then(() => {
 		refresh();
@@ -67,7 +68,7 @@ function remove(inbox: string) {
 }
 
 function refresh() {
-	os.api('admin/relays/list').then(relayList => {
+	misskeyApi('admin/relays/list').then(relayList => {
 		relays.value = relayList;
 	});
 }
diff --git a/packages/frontend/src/pages/admin/roles.edit.vue b/packages/frontend/src/pages/admin/roles.edit.vue
index 585b50aad6..db0acae24a 100644
--- a/packages/frontend/src/pages/admin/roles.edit.vue
+++ b/packages/frontend/src/pages/admin/roles.edit.vue
@@ -28,6 +28,7 @@ import { v4 as uuid } from 'uuid';
 import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { useRouter } from '@/router.js';
@@ -44,7 +45,7 @@ const role = ref<Misskey.entities.Role | null>(null);
 const data = ref<any>(null);
 
 if (props.id) {
-	role.value = await os.api('admin/roles/show', {
+	role.value = await misskeyApi('admin/roles/show', {
 		roleId: props.id,
 	});
 
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 9aa7d8dd3c..d5ce190ef2 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -67,6 +67,7 @@ import XHeader from './_header_.vue';
 import XEditor from './roles.editor.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { useRouter } from '@/router.js';
@@ -92,7 +93,7 @@ const usersPagination = {
 
 const expandedItems = ref([]);
 
-const role = reactive(await os.api('admin/roles/show', {
+const role = reactive(await misskeyApi('admin/roles/show', {
 	roleId: props.id,
 }));
 
diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue
index 3962e04218..f7c4048b23 100644
--- a/packages/frontend/src/pages/admin/roles.vue
+++ b/packages/frontend/src/pages/admin/roles.vue
@@ -233,6 +233,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { instance } from '@/instance.js';
@@ -243,7 +244,7 @@ import { ROLE_POLICIES } from '@/const.js';
 const router = useRouter();
 const baseRoleQ = ref('');
 
-const roles = await os.api('admin/roles/list');
+const roles = await misskeyApi('admin/roles/list');
 
 const policies = reactive<Record<typeof ROLE_POLICIES[number], any>>({});
 for (const ROLE_POLICY of ROLE_POLICIES) {
diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue
index 8d79dea20f..ec0c6166d0 100644
--- a/packages/frontend/src/pages/admin/security.vue
+++ b/packages/frontend/src/pages/admin/security.vue
@@ -148,6 +148,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -170,7 +171,7 @@ const truemailAuthKey = ref<string | null>(null);
 const bannedEmailDomains = ref<string>('');
 
 async function init() {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	summalyProxy.value = meta.summalyProxy;
 	enableHcaptcha.value = meta.enableHcaptcha;
 	enableRecaptcha.value = meta.enableRecaptcha;
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 224028edf3..45375764b7 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -158,6 +158,7 @@ import FormSection from '@/components/form/section.vue';
 import FormSplit from '@/components/form/split.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { fetchInstance } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -184,7 +185,7 @@ const perUserListTimelineCacheMax = ref<number>(0);
 const notesPerOneAd = ref<number>(0);
 
 async function init(): Promise<void> {
-	const meta = await os.api('admin/meta');
+	const meta = await misskeyApi('admin/meta');
 	name.value = meta.name;
 	shortName.value = meta.shortName;
 	description.value = meta.description;
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 8eca403707..5632bf7caf 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -45,6 +45,7 @@ import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { $i, updateAccount } from '@/account.js';
@@ -84,7 +85,7 @@ async function read(announcement) {
 		a.isRead = true;
 		return a;
 	});
-	os.api('i/read-announcement', { announcementId: announcement.id });
+	misskeyApi('i/read-announcement', { announcementId: announcement.id });
 	updateAccount({
 		unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== announcement.id),
 	});
diff --git a/packages/frontend/src/pages/antenna-timeline.vue b/packages/frontend/src/pages/antenna-timeline.vue
index cba54790ce..d96ca4208b 100644
--- a/packages/frontend/src/pages/antenna-timeline.vue
+++ b/packages/frontend/src/pages/antenna-timeline.vue
@@ -29,6 +29,7 @@ import * as Misskey from 'misskey-js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { scroll } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -73,7 +74,7 @@ function focus() {
 }
 
 watch(() => props.antennaId, async () => {
-	antenna.value = await os.api('antennas/show', {
+	antenna.value = await misskeyApi('antennas/show', {
 		antennaId: props.antennaId,
 	});
 }, { immediate: true });
diff --git a/packages/frontend/src/pages/api-console.vue b/packages/frontend/src/pages/api-console.vue
index 0cd4a8dae8..6456965499 100644
--- a/packages/frontend/src/pages/api-console.vue
+++ b/packages/frontend/src/pages/api-console.vue
@@ -41,7 +41,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const body = ref('{}');
@@ -51,14 +51,14 @@ const sending = ref(false);
 const res = ref('');
 const withCredential = ref(true);
 
-os.api('endpoints').then(endpointResponse => {
+misskeyApi('endpoints').then(endpointResponse => {
 	endpoints.value = endpointResponse;
 });
 
 function send() {
 	sending.value = true;
 	const requestBody = JSON5.parse(body.value);
-	os.api(endpoint.value as keyof Endpoints, requestBody, requestBody.i || (withCredential.value ? undefined : null)).then(resp => {
+	misskeyApi(endpoint.value as keyof Endpoints, requestBody, requestBody.i || (withCredential.value ? undefined : null)).then(resp => {
 		sending.value = false;
 		res.value = JSON5.stringify(resp, null, 2);
 	}, err => {
@@ -68,7 +68,7 @@ function send() {
 }
 
 function onEndpointChange() {
-	os.api('endpoint', { endpoint: endpoint.value }, withCredential.value ? undefined : null).then(resp => {
+	misskeyApi('endpoint', { endpoint: endpoint.value }, withCredential.value ? undefined : null).then(resp => {
 		const endpointBody = {};
 		for (const p of resp.params) {
 			endpointBody[p.name] =
diff --git a/packages/frontend/src/pages/auth.form.vue b/packages/frontend/src/pages/auth.form.vue
index 8a17e5895d..39a7924f94 100644
--- a/packages/frontend/src/pages/auth.form.vue
+++ b/packages/frontend/src/pages/auth.form.vue
@@ -23,7 +23,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -44,7 +44,7 @@ const name = computed(() => {
 });
 
 function cancel() {
-	os.api('auth/deny', {
+	misskeyApi('auth/deny', {
 		token: props.session.token,
 	}).then(() => {
 		emit('denied');
@@ -52,7 +52,7 @@ function cancel() {
 }
 
 function accept() {
-	os.api('auth/accept', {
+	misskeyApi('auth/accept', {
 		token: props.session.token,
 	}).then(() => {
 		emit('accepted');
diff --git a/packages/frontend/src/pages/auth.vue b/packages/frontend/src/pages/auth.vue
index 1b342647fb..fd38e22ce8 100644
--- a/packages/frontend/src/pages/auth.vue
+++ b/packages/frontend/src/pages/auth.vue
@@ -46,7 +46,7 @@ import { onMounted, ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import XForm from './auth.form.vue';
 import MkSignin from '@/components/MkSignin.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i, login } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -75,13 +75,13 @@ onMounted(async () => {
 	if (!$i) return;
 
 	try {
-		session.value = await os.api('auth/session/show', {
+		session.value = await misskeyApi('auth/session/show', {
 			token: props.token,
 		});
 
 		// 既に連携していた場合
 		if (session.value.app.isAuthorized) {
-			await os.api('auth/accept', {
+			await misskeyApi('auth/accept', {
 				token: session.value.token,
 			});
 			accepted();
diff --git a/packages/frontend/src/pages/avatar-decorations.vue b/packages/frontend/src/pages/avatar-decorations.vue
index 87964ac697..376679fd17 100644
--- a/packages/frontend/src/pages/avatar-decorations.vue
+++ b/packages/frontend/src/pages/avatar-decorations.vue
@@ -40,6 +40,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkFolder from '@/components/MkFolder.vue';
@@ -63,7 +64,7 @@ function del(avatarDecoration) {
 	}).then(({ canceled }) => {
 		if (canceled) return;
 		avatarDecorations.value = avatarDecorations.value.filter(x => x !== avatarDecoration);
-		os.api('admin/avatar-decorations/delete', avatarDecoration);
+		misskeyApi('admin/avatar-decorations/delete', avatarDecoration);
 	});
 }
 
@@ -77,7 +78,7 @@ async function save(avatarDecoration) {
 }
 
 function load() {
-	os.api('admin/avatar-decorations/list').then(_avatarDecorations => {
+	misskeyApi('admin/avatar-decorations/list').then(_avatarDecorations => {
 		avatarDecorations.value = _avatarDecorations;
 	});
 }
diff --git a/packages/frontend/src/pages/channel-editor.vue b/packages/frontend/src/pages/channel-editor.vue
index fcbd03553d..727778b6e6 100644
--- a/packages/frontend/src/pages/channel-editor.vue
+++ b/packages/frontend/src/pages/channel-editor.vue
@@ -76,6 +76,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkColorInput from '@/components/MkColorInput.vue';
 import { selectFile } from '@/scripts/select-file.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -105,7 +106,7 @@ watch(() => bannerId.value, async () => {
 	if (bannerId.value == null) {
 		bannerUrl.value = null;
 	} else {
-		bannerUrl.value = (await os.api('drive/files/show', {
+		bannerUrl.value = (await misskeyApi('drive/files/show', {
 			fileId: bannerId.value,
 		})).url;
 	}
@@ -114,7 +115,7 @@ watch(() => bannerId.value, async () => {
 async function fetchChannel() {
 	if (props.channelId == null) return;
 
-	channel.value = await os.api('channels/show', {
+	channel.value = await misskeyApi('channels/show', {
 		channelId: props.channelId,
 	});
 
@@ -179,7 +180,7 @@ async function archive() {
 
 	if (canceled) return;
 
-	os.api('channels/update', {
+	misskeyApi('channels/update', {
 		channelId: props.channelId,
 		isArchived: true,
 	}).then(() => {
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index af09189654..667563bd16 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -74,6 +74,7 @@ import MkPostForm from '@/components/MkPostForm.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import XChannelFollowButton from '@/components/MkChannelFollowButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 import { $i, iAmModerator } from '@/account.js';
 import { i18n } from '@/i18n.js';
@@ -113,7 +114,7 @@ const featuredPagination = computed(() => ({
 }));
 
 watch(() => props.channelId, async () => {
-	channel.value = await os.api('channels/show', {
+	channel.value = await misskeyApi('channels/show', {
 		channelId: props.channelId,
 	});
 	favorited.value = channel.value.isFavorited ?? false;
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index ec9876f70c..e55e99a6fa 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -32,6 +32,7 @@ import MkNotes from '@/components/MkNotes.vue';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { url } from '@/config.js';
 import MkButton from '@/components/MkButton.vue';
@@ -56,7 +57,7 @@ const pagination = {
 const isOwned = computed<boolean | null>(() => $i && clip.value && ($i.id === clip.value.userId));
 
 watch(() => props.clipId, async () => {
-	clip.value = await os.api('clips/show', {
+	clip.value = await misskeyApi('clips/show', {
 		clipId: props.clipId,
 	});
 	favorited.value = clip.value.isFavorited;
diff --git a/packages/frontend/src/pages/custom-emojis-manager.vue b/packages/frontend/src/pages/custom-emojis-manager.vue
index 011857688d..47594bad4d 100644
--- a/packages/frontend/src/pages/custom-emojis-manager.vue
+++ b/packages/frontend/src/pages/custom-emojis-manager.vue
@@ -82,6 +82,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSplit from '@/components/form/split.vue';
 import { selectFile } from '@/scripts/select-file.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -177,7 +178,7 @@ const menu = (ev: MouseEvent) => {
 		icon: 'ti ti-download',
 		text: i18n.ts.export,
 		action: async () => {
-			os.api('export-custom-emojis', {
+			misskeyApi('export-custom-emojis', {
 			})
 				.then(() => {
 					os.alert({
@@ -196,7 +197,7 @@ const menu = (ev: MouseEvent) => {
 		text: i18n.ts.import,
 		action: async () => {
 			const file = await selectFile(ev.currentTarget ?? ev.target);
-			os.api('admin/emoji/import-zip', {
+			misskeyApi('admin/emoji/import-zip', {
 				fileId: file.id,
 			})
 				.then(() => {
diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue
index 1a2fc197f9..4c635028f3 100644
--- a/packages/frontend/src/pages/drive.file.info.vue
+++ b/packages/frontend/src/pages/drive.file.info.vue
@@ -79,6 +79,7 @@ import bytes from '@/filters/bytes.js';
 import { infoImageUrl } from '@/instance.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 
 const router = useRouter();
@@ -94,7 +95,7 @@ const isImage = computed(() => file.value?.type.startsWith('image/'));
 async function fetch() {
 	fetching.value = true;
 
-	file.value = await os.api('drive/files/show', {
+	file.value = await misskeyApi('drive/files/show', {
 		fileId: props.fileId,
 	}).catch((err) => {
 		console.error(err);
diff --git a/packages/frontend/src/pages/emoji-edit-dialog.vue b/packages/frontend/src/pages/emoji-edit-dialog.vue
index 07b44a1051..e00953c1ff 100644
--- a/packages/frontend/src/pages/emoji-edit-dialog.vue
+++ b/packages/frontend/src/pages/emoji-edit-dialog.vue
@@ -39,7 +39,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 				</MkInput>
 				<MkInput v-model="aliases" autocapitalize="off">
 					<template #label>{{ i18n.ts.tags }}</template>
-					<template #caption>{{ i18n.ts.setMultipleBySeparatingWithSpace }}</template>
+					<template #caption>
+						{{ i18n.ts.theKeywordWhenSearchingForCustomEmoji }}<br/>
+						{{ i18n.ts.setMultipleBySeparatingWithSpace }}
+					</template>
 				</MkInput>
 				<MkInput v-model="license">
 					<template #label>{{ i18n.ts.license }}</template>
@@ -82,6 +85,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { customEmojiCategories } from '@/custom-emojis.js';
 import MkSwitch from '@/components/MkSwitch.vue';
@@ -104,7 +108,7 @@ const rolesThatCanBeUsedThisEmojiAsReaction = ref<Misskey.entities.Role[]>([]);
 const file = ref<Misskey.entities.DriveFile>();
 
 watch(roleIdsThatCanBeUsedThisEmojiAsReaction, async () => {
-	rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => os.api('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
+	rolesThatCanBeUsedThisEmojiAsReaction.value = (await Promise.all(roleIdsThatCanBeUsedThisEmojiAsReaction.value.map((id) => misskeyApi('admin/roles/show', { roleId: id }).catch(() => null)))).filter(x => x != null);
 }, { immediate: true });
 
 const imgUrl = computed(() => file.value ? file.value.url : props.emoji ? `/emoji/${props.emoji.name}.webp` : null);
@@ -123,7 +127,7 @@ async function changeImage(ev) {
 }
 
 async function addRole() {
-	const roles = await os.api('admin/roles/list');
+	const roles = await misskeyApi('admin/roles/list');
 	const currentRoleIds = rolesThatCanBeUsedThisEmojiAsReaction.value.map(x => x.id);
 
 	const { canceled, result: role } = await os.select({
@@ -185,7 +189,7 @@ async function del() {
 	});
 	if (canceled) return;
 
-	os.api('admin/emoji/delete', {
+	misskeyApi('admin/emoji/delete', {
 		id: props.emoji.id,
 	}).then(() => {
 		emit('done', {
diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue
index 9ba9047ca3..ea6947bbba 100644
--- a/packages/frontend/src/pages/emojis.emoji.vue
+++ b/packages/frontend/src/pages/emojis.emoji.vue
@@ -16,6 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { } from 'vue';
 import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { i18n } from '@/i18n.js';
 
@@ -43,7 +44,7 @@ function menu(ev) {
 		text: i18n.ts.info,
 		icon: 'ti ti-info-circle',
 		action: () => {
-			os.apiGet('emoji', { name: props.emoji.name }).then(res => {
+			misskeyApiGet('emoji', { name: props.emoji.name }).then(res => {
 				os.alert({
 					type: 'info',
 					text: `Name: ${res.name}\nAliases: ${res.aliases.join(' ')}\nCategory: ${res.category}\nisSensitive: ${res.isSensitive}\nlocalOnly: ${res.localOnly}\nLicense: ${res.license}\nURL: ${res.url}`,
diff --git a/packages/frontend/src/pages/explore.roles.vue b/packages/frontend/src/pages/explore.roles.vue
index d30e107e97..f06bd0840c 100644
--- a/packages/frontend/src/pages/explore.roles.vue
+++ b/packages/frontend/src/pages/explore.roles.vue
@@ -15,11 +15,11 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const roles = ref<Misskey.entities.Role[] | null>(null);
 
-os.api('roles/list').then(res => {
+misskeyApi('roles/list').then(res => {
 	roles.value = res.filter(x => x.target === 'manual').sort((a, b) => b.displayOrder - a.displayOrder);
 });
 </script>
diff --git a/packages/frontend/src/pages/explore.users.vue b/packages/frontend/src/pages/explore.users.vue
index 73c2a94fc0..440bd0b945 100644
--- a/packages/frontend/src/pages/explore.users.vue
+++ b/packages/frontend/src/pages/explore.users.vue
@@ -68,7 +68,7 @@ import * as Misskey from 'misskey-js';
 import MkUserList from '@/components/MkUserList.vue';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import MkTab from '@/components/MkTab.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -123,14 +123,14 @@ const recentlyRegisteredUsersF = { endpoint: 'users', limit: 10, noPaging: true,
 	sort: '+createdAt',
 } };
 
-os.api('hashtags/list', {
+misskeyApi('hashtags/list', {
 	sort: '+attachedLocalUsers',
 	attachedToLocalUserOnly: true,
 	limit: 30,
 }).then(tags => {
 	tagsLocal.value = tags;
 });
-os.api('hashtags/list', {
+misskeyApi('hashtags/list', {
 	sort: '+attachedRemoteUsers',
 	attachedToRemoteUserOnly: true,
 	limit: 30,
diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue
index 147a381c98..ce077779c8 100644
--- a/packages/frontend/src/pages/flash/flash-edit.vue
+++ b/packages/frontend/src/pages/flash/flash-edit.vue
@@ -38,6 +38,7 @@ import { computed, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkTextarea from '@/components/MkTextarea.vue';
@@ -369,7 +370,7 @@ const flash = ref<Misskey.entities.Flash | null>(null);
 const visibility = ref<Misskey.entities.FlashUpdateRequest['visibility']>('public');
 
 if (props.id) {
-	flash.value = await os.api('flash/show', {
+	flash.value = await misskeyApi('flash/show', {
 		flashId: props.id,
 	});
 }
diff --git a/packages/frontend/src/pages/flash/flash.vue b/packages/frontend/src/pages/flash/flash.vue
index 6df9bbc241..bcffb5601a 100644
--- a/packages/frontend/src/pages/flash/flash.vue
+++ b/packages/frontend/src/pages/flash/flash.vue
@@ -62,12 +62,13 @@ import * as Misskey from 'misskey-js';
 import { Interpreter, Parser, values } from '@syuilo/aiscript';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { url } from '@/config.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkAsUi from '@/components/MkAsUi.vue';
 import { AsUiComponent, AsUiRoot, registerAsUiLib } from '@/scripts/aiscript/ui.js';
-import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkCode from '@/components/MkCode.vue';
 import { defaultStore } from '@/store.js';
@@ -84,7 +85,7 @@ const error = ref<any>(null);
 
 function fetchFlash() {
 	flash.value = null;
-	os.api('flash/show', {
+	misskeyApi('flash/show', {
 		flashId: props.id,
 	}).then(_flash => {
 		flash.value = _flash;
@@ -162,15 +163,7 @@ async function run() {
 		THIS_ID: values.STR(flash.value.id),
 		THIS_URL: values.STR(`${url}/play/${flash.value.id}`),
 	}, {
-		in: (q) => {
-			return new Promise(ok => {
-				os.inputText({
-					title: q,
-				}).then(({ result: a }) => {
-					ok(a ?? '');
-				});
-			});
-		},
+		in: aiScriptReadline,
 		out: (value) => {
 			// nop
 		},
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index 51f31b1ca5..902e6a4b51 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -41,7 +41,7 @@ import { shallowRef, computed } from 'vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import { userPage, acct } from '@/filters/user.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { infoImageUrl } from '@/instance.js';
@@ -54,13 +54,13 @@ const pagination = {
 };
 
 function accept(user) {
-	os.api('following/requests/accept', { userId: user.id }).then(() => {
+	misskeyApi('following/requests/accept', { userId: user.id }).then(() => {
 		paginationComponent.value.reload();
 	});
 }
 
 function reject(user) {
-	os.api('following/requests/reject', { userId: user.id }).then(() => {
+	misskeyApi('following/requests/reject', { userId: user.id }).then(() => {
 		paginationComponent.value.reload();
 	});
 }
diff --git a/packages/frontend/src/pages/follow.vue b/packages/frontend/src/pages/follow.vue
index a0a4a480b5..5a21604080 100644
--- a/packages/frontend/src/pages/follow.vue
+++ b/packages/frontend/src/pages/follow.vue
@@ -12,9 +12,10 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { mainRouter } from '@/router.js';
 import { i18n } from '@/i18n.js';
-import { defaultStore } from "@/store.js";
+import { defaultStore } from '@/store.js';
 
 async function follow(user): Promise<void> {
 	const { canceled } = await os.confirm({
@@ -42,7 +43,7 @@ if (acct == null) {
 let promise;
 
 if (acct.startsWith('https://')) {
-	promise = os.api('ap/show', {
+	promise = misskeyApi('ap/show', {
 		uri: acct,
 	});
 	promise.then(res => {
@@ -60,7 +61,7 @@ if (acct.startsWith('https://')) {
 		}
 	});
 } else {
-	promise = os.api('users/show', Misskey.acct.parse(acct));
+	promise = misskeyApi('users/show', Misskey.acct.parse(acct));
 	promise.then(user => {
 		follow(user);
 	});
diff --git a/packages/frontend/src/pages/gallery/edit.vue b/packages/frontend/src/pages/gallery/edit.vue
index d711cb4e88..e0c7654531 100644
--- a/packages/frontend/src/pages/gallery/edit.vue
+++ b/packages/frontend/src/pages/gallery/edit.vue
@@ -47,6 +47,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSuspense from '@/components/form/suspense.vue';
 import { selectFiles } from '@/scripts/select-file.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -107,7 +108,7 @@ async function del() {
 }
 
 watch(() => props.postId, () => {
-	init.value = () => props.postId ? os.api('gallery/posts/show', {
+	init.value = () => props.postId ? misskeyApi('gallery/posts/show', {
 		postId: props.postId,
 	}).then(post => {
 		files.value = post.files ?? [];
diff --git a/packages/frontend/src/pages/gallery/post.vue b/packages/frontend/src/pages/gallery/post.vue
index 77af81cec1..f71fe0f260 100644
--- a/packages/frontend/src/pages/gallery/post.vue
+++ b/packages/frontend/src/pages/gallery/post.vue
@@ -66,6 +66,7 @@ import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import MkGalleryPostPreview from '@/components/MkGalleryPostPreview.vue';
@@ -97,7 +98,7 @@ const otherPostsPagination = {
 
 function fetchPost() {
 	post.value = null;
-	os.api('gallery/posts/show', {
+	misskeyApi('gallery/posts/show', {
 		postId: props.postId,
 	}).then(_post => {
 		post.value = _post;
diff --git a/packages/frontend/src/pages/install-extentions.vue b/packages/frontend/src/pages/install-extentions.vue
index 8117699849..3815658bd4 100644
--- a/packages/frontend/src/pages/install-extentions.vue
+++ b/packages/frontend/src/pages/install-extentions.vue
@@ -105,6 +105,7 @@ import MkInfo from '@/components/MkInfo.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { AiScriptPluginMeta, parsePluginMeta, installPlugin } from '@/scripts/install-plugin.js';
 import { parseThemeCode, installTheme } from '@/scripts/install-theme.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
@@ -159,7 +160,7 @@ async function fetch() {
 		uiPhase.value = 'error';
 		return;
 	}
-	const res = await os.api('fetch-external-resources', {
+	const res = await misskeyApi('fetch-external-resources', {
 		url: url.value,
 		hash: hash.value,
 	}).catch((err) => {
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 97dc0a8633..c8a0eeeeaa 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -129,6 +129,7 @@ import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import number from '@/filters/number.js';
 import { iAmModerator, iAmAdmin } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -164,9 +165,9 @@ const usersPagination = {
 
 async function fetch(): Promise<void> {
 	if (iAmAdmin) {
-		meta.value = await os.api('admin/meta');
+		meta.value = await misskeyApi('admin/meta');
 	}
-	instance.value = await os.api('federation/show-instance', {
+	instance.value = await misskeyApi('federation/show-instance', {
 		host: props.host,
 	});
 	suspended.value = instance.value?.isSuspended ?? false;
@@ -179,7 +180,7 @@ async function toggleBlock(): Promise<void> {
 	if (!meta.value) throw new Error('No meta?');
 	if (!instance.value) throw new Error('No instance?');
 	const { host } = instance.value;
-	await os.api('admin/update-meta', {
+	await misskeyApi('admin/update-meta', {
 		blockedHosts: isBlocked.value ? meta.value.blockedHosts.concat([host]) : meta.value.blockedHosts.filter(x => x !== host),
 	});
 }
@@ -189,14 +190,14 @@ async function toggleSilenced(): Promise<void> {
 	if (!instance.value) throw new Error('No instance?');
 	const { host } = instance.value;
 	const silencedHosts = meta.value.silencedHosts ?? [];
-	await os.api('admin/update-meta', {
+	await misskeyApi('admin/update-meta', {
 		silencedHosts: isSilenced.value ? silencedHosts.concat([host]) : silencedHosts.filter(x => x !== host),
 	});
 }
 
 async function toggleSuspend(): Promise<void> {
 	if (!instance.value) throw new Error('No instance?');
-	await os.api('admin/federation/update-instance', {
+	await misskeyApi('admin/federation/update-instance', {
 		host: instance.value.host,
 		isSuspended: suspended.value,
 	});
@@ -204,7 +205,7 @@ async function toggleSuspend(): Promise<void> {
 
 function refreshMetadata(): void {
 	if (!instance.value) throw new Error('No instance?');
-	os.api('admin/federation/refresh-remote-instance-metadata', {
+	misskeyApi('admin/federation/refresh-remote-instance-metadata', {
 		host: instance.value.host,
 	});
 	os.alert({
diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue
index 25ce38e0ef..61030741fa 100644
--- a/packages/frontend/src/pages/invite.vue
+++ b/packages/frontend/src/pages/invite.vue
@@ -40,6 +40,7 @@ import { computed, ref, shallowRef } from 'vue';
 import type * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkButton from '@/components/MkButton.vue';
 import MkPagination, { Paging } from '@/components/MkPagination.vue';
 import MkInviteCode from '@/components/MkInviteCode.vue';
@@ -68,7 +69,7 @@ const resetCycle = computed<null | string>(() => {
 });
 
 async function create() {
-	const ticket = await os.api('invite/create');
+	const ticket = await misskeyApi('invite/create');
 	os.alert({
 		type: 'success',
 		title: i18n.ts.inviteCodeCreated,
@@ -87,7 +88,7 @@ function deleted(id: string) {
 }
 
 async function update() {
-	currentInviteLimit.value = (await os.api('invite/limit')).remaining;
+	currentInviteLimit.value = (await misskeyApi('invite/limit')).remaining;
 }
 
 update();
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 936d078848..03db02a350 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -37,6 +37,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { watch, computed, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { userPage } from '@/filters/user.js';
 import { i18n } from '@/i18n.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
@@ -53,12 +54,12 @@ const error = ref();
 const users = ref<Misskey.entities.UserDetailed[]>([]);
 
 function fetchList(): void {
-	os.api('users/lists/show', {
+	misskeyApi('users/lists/show', {
 		listId: props.listId,
 		forPublic: true,
 	}).then(_list => {
 		list.value = _list;
-		os.api('users/show', {
+		misskeyApi('users/show', {
 			userIds: list.value.userIds,
 		}).then(_users => {
 			users.value = _users;
diff --git a/packages/frontend/src/pages/miauth.vue b/packages/frontend/src/pages/miauth.vue
index ad9bea4548..539cb462ad 100644
--- a/packages/frontend/src/pages/miauth.vue
+++ b/packages/frontend/src/pages/miauth.vue
@@ -46,7 +46,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, computed } from 'vue';
 import MkSignin from '@/components/MkSignin.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i, login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -65,7 +65,7 @@ const state = ref<string | null>(null);
 
 async function accept(): Promise<void> {
 	state.value = 'waiting';
-	await os.api('miauth/gen-token', {
+	await misskeyApi('miauth/gen-token', {
 		session: props.session,
 		name: props.name,
 		iconUrl: props.icon,
diff --git a/packages/frontend/src/pages/my-antennas/edit.vue b/packages/frontend/src/pages/my-antennas/edit.vue
index 9b3d56ee36..0648f5340f 100644
--- a/packages/frontend/src/pages/my-antennas/edit.vue
+++ b/packages/frontend/src/pages/my-antennas/edit.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XAntenna from './editor.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -32,7 +32,7 @@ function onAntennaUpdated() {
 	router.push('/my/antennas');
 }
 
-os.api('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => {
+misskeyApi('antennas/show', { antennaId: props.antennaId }).then((antennaResponse) => {
 	antenna.value = antennaResponse;
 });
 
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 9b19206d96..45acbb2158 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -57,6 +57,7 @@ import MkTextarea from '@/components/MkTextarea.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -84,7 +85,7 @@ const userLists = ref<Misskey.entities.UserList[] | null>(null);
 
 watch(() => src.value, async () => {
 	if (src.value === 'list' && userLists.value === null) {
-		userLists.value = await os.api('users/lists/list');
+		userLists.value = await misskeyApi('users/lists/list');
 	}
 });
 
@@ -119,7 +120,7 @@ async function deleteAntenna() {
 	});
 	if (canceled) return;
 
-	await os.api('antennas/delete', {
+	await misskeyApi('antennas/delete', {
 		antennaId: props.antenna.id,
 	});
 
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index bc09e916e3..850222708e 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -32,6 +32,7 @@ import MkPagination from '@/components/MkPagination.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkClipPreview from '@/components/MkClipPreview.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { clipsCache } from '@/cache.js';
@@ -48,7 +49,7 @@ const favorites = ref<Misskey.entities.Clip[] | null>(null);
 const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>();
 
 watch(tab, async () => {
-	favorites.value = await os.api('clips/my-favorites');
+	favorites.value = await misskeyApi('clips/my-favorites');
 });
 
 async function create() {
diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue
index 0abfb15d98..14e2315843 100644
--- a/packages/frontend/src/pages/my-lists/index.vue
+++ b/packages/frontend/src/pages/my-lists/index.vue
@@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 			<div v-if="items.length > 0" class="_gaps">
 				<MkA v-for="list in items" :key="list.id" class="_panel" :class="$style.list" :to="`/my/lists/${ list.id }`">
-					<div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.t('nUsers', { n: `${list.userIds.length}/${$i?.policies['userEachUserListsLimit']}` }) }})</span></div>
+					<div style="margin-bottom: 4px;">{{ list.name }} <span :class="$style.nUsers">({{ i18n.t('nUsers', { n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }})</span></div>
 					<MkAvatars :userIds="list.userIds" :limit="10"/>
 				</MkA>
 			</div>
@@ -37,7 +37,9 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { userListsCache } from '@/cache.js';
 import { infoImageUrl } from '@/instance.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
 
 const items = computed(() => userListsCache.value.value ?? []);
 
diff --git a/packages/frontend/src/pages/my-lists/list.vue b/packages/frontend/src/pages/my-lists/list.vue
index cf9da02868..5798070ad8 100644
--- a/packages/frontend/src/pages/my-lists/list.vue
+++ b/packages/frontend/src/pages/my-lists/list.vue
@@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 			<MkFolder defaultOpen>
 				<template #label>{{ i18n.ts.members }}</template>
-				<template #caption>{{ i18n.t('nUsers', { n: `${list.userIds.length}/${$i?.policies['userEachUserListsLimit']}` }) }}</template>
+				<template #caption>{{ i18n.t('nUsers', { n: `${list.userIds.length}/${$i.policies['userEachUserListsLimit']}` }) }}</template>
 
 				<div class="_gaps_s">
 					<MkButton rounded primary style="margin: 0 auto;" @click="addUser()">{{ i18n.ts.addUser }}</MkButton>
@@ -57,6 +57,7 @@ import { computed, ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { mainRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -66,10 +67,12 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkInput from '@/components/MkInput.vue';
 import { userListsCache } from '@/cache.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import { defaultStore } from '@/store.js';
 import MkPagination from '@/components/MkPagination.vue';
 
+const $i = signinRequired();
+
 const {
 	enableInfiniteScroll,
 } = defaultStore.reactiveState;
@@ -91,7 +94,7 @@ const membershipsPagination = {
 };
 
 function fetchList() {
-	os.api('users/lists/show', {
+	misskeyApi('users/lists/show', {
 		listId: props.listId,
 	}).then(_list => {
 		list.value = _list;
@@ -119,7 +122,7 @@ async function removeUser(item, ev) {
 		danger: true,
 		action: async () => {
 			if (!list.value) return;
-			os.api('users/lists/pull', {
+			misskeyApi('users/lists/pull', {
 				listId: list.value.id,
 				userId: item.userId,
 			}).then(() => {
@@ -134,7 +137,7 @@ async function showMembershipMenu(item, ev) {
 		text: item.withReplies ? i18n.ts.hideRepliesToOthersInTimeline : i18n.ts.showRepliesToOthersInTimeline,
 		icon: item.withReplies ? 'ti ti-messages-off' : 'ti ti-messages',
 		action: async () => {
-			os.api('users/lists/update-membership', {
+			misskeyApi('users/lists/update-membership', {
 				listId: list.value.id,
 				userId: item.userId,
 				withReplies: !item.withReplies,
diff --git a/packages/frontend/src/pages/note.vue b/packages/frontend/src/pages/note.vue
index ff1e80aaab..7e353e629c 100644
--- a/packages/frontend/src/pages/note.vue
+++ b/packages/frontend/src/pages/note.vue
@@ -50,7 +50,7 @@ import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
 import MkNotes from '@/components/MkNotes.vue';
 import MkRemoteCaution from '@/components/MkRemoteCaution.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import { dateString } from '@/filters/date.js';
@@ -90,13 +90,13 @@ function fetchNote() {
 	showPrev.value = false;
 	showNext.value = false;
 	note.value = null;
-	os.api('notes/show', {
+	misskeyApi('notes/show', {
 		noteId: props.noteId,
 	}).then(res => {
 		note.value = res;
 		// 古いノートは被クリップ数をカウントしていないので、2023-10-01以前のものは強制的にnotes/clipsを叩く
 		if (note.value.clippedCount > 0 || new Date(note.value.createdAt).getTime() < new Date('2023-10-01').getTime()) {
-			os.api('notes/clips', {
+			misskeyApi('notes/clips', {
 				noteId: note.value.id,
 			}).then((_clips) => {
 				clips.value = _clips;
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
index 9d6da653b4..561b0142aa 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.image.vue
@@ -26,6 +26,7 @@ import * as Misskey from 'misskey-js';
 import XContainer from '../page-editor.container.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -52,7 +53,7 @@ onMounted(async () => {
 	if (props.modelValue.fileId == null) {
 		await choose();
 	} else {
-		os.api('drive/files/show', {
+		misskeyApi('drive/files/show', {
 			fileId: props.modelValue.fileId,
 		}).then(fileResponse => {
 			file.value = fileResponse;
diff --git a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
index ea9d52c2be..ebabc98d23 100644
--- a/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
+++ b/packages/frontend/src/pages/page-editor/els/page-editor.el.note.vue
@@ -30,7 +30,7 @@ import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkNote from '@/components/MkNote.vue';
 import MkNoteDetailed from '@/components/MkNoteDetailed.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -53,7 +53,7 @@ watch(id, async () => {
 		...props.modelValue,
 		note: id.value,
 	});
-	note.value = await os.api('notes/show', { noteId: id.value });
+	note.value = await misskeyApi('notes/show', { noteId: id.value });
 }, {
 	immediate: true,
 });
diff --git a/packages/frontend/src/pages/page-editor/page-editor.vue b/packages/frontend/src/pages/page-editor/page-editor.vue
index bcfbf5825f..496a8c3274 100644
--- a/packages/frontend/src/pages/page-editor/page-editor.vue
+++ b/packages/frontend/src/pages/page-editor/page-editor.vue
@@ -71,6 +71,7 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkInput from '@/components/MkInput.vue';
 import { url } from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { selectFile } from '@/scripts/select-file.js';
 import { mainRouter } from '@/router.js';
 import { i18n } from '@/i18n.js';
@@ -106,7 +107,7 @@ watch(eyeCatchingImageId, async () => {
 	if (eyeCatchingImageId.value == null) {
 		eyeCatchingImage.value = null;
 	} else {
-		eyeCatchingImage.value = await os.api('drive/files/show', {
+		eyeCatchingImage.value = await misskeyApi('drive/files/show', {
 			fileId: eyeCatchingImageId.value,
 		});
 	}
@@ -149,7 +150,7 @@ function save() {
 
 	if (pageId.value) {
 		options.pageId = pageId.value;
-		os.api('pages/update', options)
+		misskeyApi('pages/update', options)
 			.then(page => {
 				currentName.value = name.value.trim();
 				os.alert({
@@ -158,7 +159,7 @@ function save() {
 				});
 			}).catch(onError);
 	} else {
-		os.api('pages/create', options)
+		misskeyApi('pages/create', options)
 			.then(created => {
 				pageId.value = created.id;
 				currentName.value = name.value.trim();
@@ -177,7 +178,7 @@ function del() {
 		text: i18n.t('removeAreYouSure', { x: title.value.trim() }),
 	}).then(({ canceled }) => {
 		if (canceled) return;
-		os.api('pages/delete', {
+		misskeyApi('pages/delete', {
 			pageId: pageId.value,
 		}).then(() => {
 			os.alert({
@@ -192,7 +193,7 @@ function del() {
 function duplicate() {
 	title.value = title.value + ' - copy';
 	name.value = name.value + '-copy';
-	os.api('pages/create', getSaveOptions()).then(created => {
+	misskeyApi('pages/create', getSaveOptions()).then(created => {
 		pageId.value = created.id;
 		currentName.value = name.value.trim();
 		os.alert({
@@ -236,11 +237,11 @@ function removeEyeCatchingImage() {
 
 async function init() {
 	if (props.initPageId) {
-		page.value = await os.api('pages/show', {
+		page.value = await misskeyApi('pages/show', {
 			pageId: props.initPageId,
 		});
 	} else if (props.initPageName && props.initUser) {
-		page.value = await os.api('pages/show', {
+		page.value = await misskeyApi('pages/show', {
 			name: props.initPageName,
 			username: props.initUser,
 		});
diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue
index 11c8e15e14..7b4f06d5cf 100644
--- a/packages/frontend/src/pages/page.vue
+++ b/packages/frontend/src/pages/page.vue
@@ -81,6 +81,7 @@ import * as Misskey from 'misskey-js';
 import XPage from '@/components/page/page.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { url } from '@/config.js';
 import MkMediaImage from '@/components/MkMediaImage.vue';
 import MkFollowButton from '@/components/MkFollowButton.vue';
@@ -113,7 +114,7 @@ const path = computed(() => props.username + '/' + props.pageName);
 
 function fetchPage() {
 	page.value = null;
-	os.api('pages/show', {
+	misskeyApi('pages/show', {
 		name: props.pageName,
 		username: props.username,
 	}).then(async _page => {
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 822a39c2e8..8ae8495de3 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -36,6 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { watch, computed, ref } from 'vue';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import FormLink from '@/components/form/link.vue';
@@ -54,7 +55,7 @@ const scope = computed(() => props.path ? props.path.split('/') : []);
 const keys = ref<any>(null);
 
 function fetchKeys() {
-	os.api('i/registry/keys-with-type', {
+	misskeyApi('i/registry/keys-with-type', {
 		scope: scope.value,
 		domain: props.domain === '@' ? null : props.domain,
 	}).then(res => {
diff --git a/packages/frontend/src/pages/registry.value.vue b/packages/frontend/src/pages/registry.value.vue
index 243c69eed5..f355550209 100644
--- a/packages/frontend/src/pages/registry.value.vue
+++ b/packages/frontend/src/pages/registry.value.vue
@@ -48,6 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { watch, computed, ref } from 'vue';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkButton from '@/components/MkButton.vue';
@@ -68,7 +69,7 @@ const value = ref<any>(null);
 const valueForEditor = ref<string | null>(null);
 
 function fetchValue() {
-	os.api('i/registry/get-detail', {
+	misskeyApi('i/registry/get-detail', {
 		scope: scope.value,
 		key: key.value,
 		domain: props.domain === '@' ? null : props.domain,
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index f45f8922ad..ca54d23b6f 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -26,6 +26,7 @@ import { ref, computed } from 'vue';
 import * as Misskey from 'misskey-js';
 import JSON5 from 'json5';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import FormLink from '@/components/form/link.vue';
@@ -35,7 +36,7 @@ import MkButton from '@/components/MkButton.vue';
 const scopesWithDomain = ref<Misskey.entities.IRegistryScopesWithDomainResponse | null>(null);
 
 function fetchScopes() {
-	os.api('i/registry/scopes-with-domain').then(res => {
+	misskeyApi('i/registry/scopes-with-domain').then(res => {
 		scopesWithDomain.value = res;
 	});
 }
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index 10642ddefe..67f18ee87e 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkUserList from '@/components/MkUserList.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -59,7 +59,7 @@ const error = ref();
 const visible = ref(false);
 
 watch(() => props.role, () => {
-	os.api('roles/show', {
+	misskeyApi('roles/show', {
 		roleId: props.role,
 	}).then(res => {
 		role.value = res;
diff --git a/packages/frontend/src/pages/scratchpad.vue b/packages/frontend/src/pages/scratchpad.vue
index 1453bc1658..31a3b4e91b 100644
--- a/packages/frontend/src/pages/scratchpad.vue
+++ b/packages/frontend/src/pages/scratchpad.vue
@@ -44,7 +44,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript';
 import MkContainer from '@/components/MkContainer.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkCodeEditor from '@/components/MkCodeEditor.vue';
-import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import * as os from '@/os.js';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
@@ -86,19 +86,7 @@ async function run() {
 			root.value = _root.value;
 		}),
 	}), {
-		in: (q) => {
-			return new Promise(ok => {
-				os.inputText({
-					title: q,
-				}).then(({ canceled, result: a }) => {
-					if (canceled) {
-						ok('');
-					} else {
-						ok(a);
-					}
-				});
-			});
-		},
+		in: aiScriptReadline,
 		out: (value) => {
 			if (value.type === 'str' && value.value.toLowerCase().replace(',', '').includes('hello world')) {
 				claimAchievement('outputHelloWorldOnScratchpad');
diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue
index 5c0b54e2d9..1b12910a38 100644
--- a/packages/frontend/src/pages/search.note.vue
+++ b/packages/frontend/src/pages/search.note.vue
@@ -49,6 +49,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
 import { useRouter } from '@/router.js';
 import MkFolder from '@/components/MkFolder.vue';
@@ -74,7 +75,7 @@ async function search() {
 	if (query == null || query === '') return;
 
 	if (query.startsWith('https://')) {
-		const promise = os.api('ap/show', {
+		const promise = misskeyApi('ap/show', {
 			uri: query,
 		});
 
diff --git a/packages/frontend/src/pages/search.user.vue b/packages/frontend/src/pages/search.user.vue
index 829c706e0e..5e9048ee57 100644
--- a/packages/frontend/src/pages/search.user.vue
+++ b/packages/frontend/src/pages/search.user.vue
@@ -33,6 +33,7 @@ import MkButton from '@/components/MkButton.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
 import MkFoldableSection from '@/components/MkFoldableSection.vue';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 
 const router = useRouter();
@@ -48,7 +49,7 @@ async function search() {
 	if (query == null || query === '') return;
 
 	if (query.startsWith('https://')) {
-		const promise = os.api('ap/show', {
+		const promise = misskeyApi('ap/show', {
 			uri: query,
 		});
 
diff --git a/packages/frontend/src/pages/settings/2fa.qrdialog.vue b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
index 4641b49103..3d7bb06da2 100644
--- a/packages/frontend/src/pages/settings/2fa.qrdialog.vue
+++ b/packages/frontend/src/pages/settings/2fa.qrdialog.vue
@@ -110,7 +110,9 @@ import * as os from '@/os.js';
 import MkFolder from '@/components/MkFolder.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import { confetti } from '@/scripts/confetti.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
 
 defineProps<{
 	twoFactorData: {
@@ -151,7 +153,7 @@ function downloadBackupCodes() {
 		const txtBlob = new Blob([backupCodes.value.join('\n')], { type: 'text/plain' });
 		const dummya = document.createElement('a');
 		dummya.href = URL.createObjectURL(txtBlob);
-		dummya.download = `${$i?.username}-2fa-backup-codes.txt`;
+		dummya.download = `${$i.username}-2fa-backup-codes.txt`;
 		dummya.click();
 	}
 }
diff --git a/packages/frontend/src/pages/settings/2fa.vue b/packages/frontend/src/pages/settings/2fa.vue
index 4c165ef4ee..35331738fd 100644
--- a/packages/frontend/src/pages/settings/2fa.vue
+++ b/packages/frontend/src/pages/settings/2fa.vue
@@ -80,9 +80,11 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import { i18n } from '@/i18n.js';
 
+const $i = signinRequired();
+
 // メモ: 各エンドポイントはmeUpdatedを発行するため、refreshAccountは不要
 
 withDefaults(defineProps<{
@@ -91,7 +93,7 @@ withDefaults(defineProps<{
 	first: false,
 });
 
-const usePasswordLessLogin = computed(() => $i?.usePasswordLessLogin ?? false);
+const usePasswordLessLogin = computed(() => $i.usePasswordLessLogin ?? false);
 
 async function registerTOTP(): Promise<void> {
 	const auth = await os.authenticateDialog();
diff --git a/packages/frontend/src/pages/settings/accounts.vue b/packages/frontend/src/pages/settings/accounts.vue
index 4320ad7e9e..1c96773b9b 100644
--- a/packages/frontend/src/pages/settings/accounts.vue
+++ b/packages/frontend/src/pages/settings/accounts.vue
@@ -24,6 +24,7 @@ import type * as Misskey from 'misskey-js';
 import FormSuspense from '@/components/form/suspense.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { getAccounts, addAccount as addAccounts, removeAccount as _removeAccount, login, $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -36,7 +37,7 @@ const init = async () => {
 	getAccounts().then(accounts => {
 		storedAccounts.value = accounts.filter(x => x.id !== $i!.id);
 
-		return os.api('users/show', {
+		return misskeyApi('users/show', {
 			userIds: storedAccounts.value.map(x => x.id),
 		});
 	}).then(response => {
diff --git a/packages/frontend/src/pages/settings/api.vue b/packages/frontend/src/pages/settings/api.vue
index eee7884aaa..35275c7dcf 100644
--- a/packages/frontend/src/pages/settings/api.vue
+++ b/packages/frontend/src/pages/settings/api.vue
@@ -16,6 +16,7 @@ import { defineAsyncComponent, ref, computed } from 'vue';
 import FormLink from '@/components/form/link.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -25,7 +26,7 @@ function generateToken() {
 	os.popup(defineAsyncComponent(() => import('@/components/MkTokenGenerateWindow.vue')), {}, {
 		done: async result => {
 			const { name, permissions } = result;
-			const { token } = await os.api('miauth/gen-token', {
+			const { token } = await misskeyApi('miauth/gen-token', {
 				session: null,
 				name: name,
 				permission: permissions,
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index 419bcd6fee..4a778d4b38 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -47,7 +47,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 <script lang="ts" setup>
 import { ref, computed } from 'vue';
 import FormPagination from '@/components/MkPagination.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkKeyValue from '@/components/MkKeyValue.vue';
@@ -66,7 +66,7 @@ const pagination = {
 };
 
 function revoke(token) {
-	os.api('i/revoke-token', { tokenId: token.id }).then(() => {
+	misskeyApi('i/revoke-token', { tokenId: token.id }).then(() => {
 		list.value.reload();
 	});
 }
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
index 9c95b5547e..29586ad5f1 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.decoration.vue
@@ -16,7 +16,9 @@ SPDX-License-Identifier: AGPL-3.0-only
 
 <script lang="ts" setup>
 import { } from 'vue';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
 
 const props = defineProps<{
 	active?: boolean;
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
index 329ab4d47a..4cd0dd10e6 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.dialog.vue
@@ -51,7 +51,9 @@ import MkModalWindow from '@/components/MkModalWindow.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import { i18n } from '@/i18n.js';
 import MkRange from '@/components/MkRange.vue';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
 
 const props = defineProps<{
 	usingIndex: number | null;
diff --git a/packages/frontend/src/pages/settings/avatar-decoration.vue b/packages/frontend/src/pages/settings/avatar-decoration.vue
index 6551fc917e..70565cc990 100644
--- a/packages/frontend/src/pages/settings/avatar-decoration.vue
+++ b/packages/frontend/src/pages/settings/avatar-decoration.vue
@@ -50,15 +50,18 @@ import * as Misskey from 'misskey-js';
 import XDecoration from './avatar-decoration.decoration.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import MkInfo from '@/components/MkInfo.vue';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
+const $i = signinRequired();
+
 const loading = ref(true);
 const avatarDecorations = ref<Misskey.entities.GetAvatarDecorationsResponse>([]);
 
-os.api('get-avatar-decorations').then(_avatarDecorations => {
+misskeyApi('get-avatar-decorations').then(_avatarDecorations => {
 	avatarDecorations.value = _avatarDecorations;
 	loading.value = false;
 });
diff --git a/packages/frontend/src/pages/settings/drive-cleaner.vue b/packages/frontend/src/pages/settings/drive-cleaner.vue
index 4efcdb31da..496c8d94dc 100644
--- a/packages/frontend/src/pages/settings/drive-cleaner.vue
+++ b/packages/frontend/src/pages/settings/drive-cleaner.vue
@@ -51,6 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, ref, watch } from 'vue';
 import tinycolor from 'tinycolor2';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkPagination from '@/components/MkPagination.vue';
 import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
 import { i18n } from '@/i18n.js';
@@ -94,7 +95,7 @@ watch(sortModeSelect, () => {
 
 function fetchDriveInfo(): void {
 	fetching.value = true;
-	os.api('drive').then(info => {
+	misskeyApi('drive').then(info => {
 		capacity.value = info.capacity;
 		usage.value = info.usage;
 		fetching.value = false;
diff --git a/packages/frontend/src/pages/settings/drive.vue b/packages/frontend/src/pages/settings/drive.vue
index 7857cf7125..c3ce89d144 100644
--- a/packages/frontend/src/pages/settings/drive.vue
+++ b/packages/frontend/src/pages/settings/drive.vue
@@ -66,12 +66,15 @@ import FormSection from '@/components/form/section.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import FormSplit from '@/components/form/split.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import bytes from '@/filters/bytes.js';
 import { defaultStore } from '@/store.js';
 import MkChart from '@/components/MkChart.vue';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+
+const $i = signinRequired();
 
 const fetching = ref(true);
 const usage = ref<number | null>(null);
@@ -81,6 +84,7 @@ const alwaysMarkNsfw = ref($i.alwaysMarkNsfw);
 const autoSensitive = ref($i.autoSensitive);
 
 const meterStyle = computed(() => {
+	if (!capacity.value || !usage.value) return {};
 	return {
 		width: `${usage.value / capacity.value * 100}%`,
 		background: tinycolor({
@@ -93,14 +97,14 @@ const meterStyle = computed(() => {
 
 const keepOriginalUploading = computed(defaultStore.makeGetterSetter('keepOriginalUploading'));
 
-os.api('drive').then(info => {
+misskeyApi('drive').then(info => {
 	capacity.value = info.capacity;
 	usage.value = info.usage;
 	fetching.value = false;
 });
 
 if (defaultStore.state.uploadFolder) {
-	os.api('drive/folders/show', {
+	misskeyApi('drive/folders/show', {
 		folderId: defaultStore.state.uploadFolder,
 	}).then(response => {
 		uploadFolder.value = response;
@@ -112,7 +116,7 @@ function chooseUploadFolder() {
 		defaultStore.set('uploadFolder', folder ? folder.id : null);
 		os.success();
 		if (defaultStore.state.uploadFolder) {
-			uploadFolder.value = await os.api('drive/folders/show', {
+			uploadFolder.value = await misskeyApi('drive/folders/show', {
 				folderId: defaultStore.state.uploadFolder,
 			});
 		} else {
@@ -122,7 +126,7 @@ function chooseUploadFolder() {
 }
 
 function saveProfile() {
-	os.api('i/update', {
+	misskeyApi('i/update', {
 		alwaysMarkNsfw: !!alwaysMarkNsfw.value,
 		autoSensitive: !!autoSensitive.value,
 	}).catch(err => {
diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue
index 309e025ada..c2205dcb0e 100644
--- a/packages/frontend/src/pages/settings/email.vue
+++ b/packages/frontend/src/pages/settings/email.vue
@@ -54,15 +54,18 @@ import MkInfo from '@/components/MkInfo.vue';
 import MkInput from '@/components/MkInput.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import { signinRequired } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { instance } from '@/instance.js';
 
-const emailAddress = ref($i!.email);
+const $i = signinRequired();
+
+const emailAddress = ref($i.email);
 
 const onChangeReceiveAnnouncementEmail = (v) => {
-	os.api('i/update', {
+	misskeyApi('i/update', {
 		receiveAnnouncementEmail: v,
 	});
 };
@@ -78,14 +81,14 @@ async function saveEmailAddress() {
 	});
 }
 
-const emailNotification_mention = ref($i!.emailNotificationTypes.includes('mention'));
-const emailNotification_reply = ref($i!.emailNotificationTypes.includes('reply'));
-const emailNotification_quote = ref($i!.emailNotificationTypes.includes('quote'));
-const emailNotification_follow = ref($i!.emailNotificationTypes.includes('follow'));
-const emailNotification_receiveFollowRequest = ref($i!.emailNotificationTypes.includes('receiveFollowRequest'));
+const emailNotification_mention = ref($i.emailNotificationTypes.includes('mention'));
+const emailNotification_reply = ref($i.emailNotificationTypes.includes('reply'));
+const emailNotification_quote = ref($i.emailNotificationTypes.includes('quote'));
+const emailNotification_follow = ref($i.emailNotificationTypes.includes('follow'));
+const emailNotification_receiveFollowRequest = ref($i.emailNotificationTypes.includes('receiveFollowRequest'));
 
 const saveNotificationSettings = () => {
-	os.api('i/update', {
+	misskeyApi('i/update', {
 		emailNotificationTypes: [
 			...[emailNotification_mention.value ? 'mention' : null],
 			...[emailNotification_reply.value ? 'reply' : null],
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index 3e5f5cb8c8..3d5fa66d9f 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -234,6 +234,7 @@ import MkInfo from '@/components/MkInfo.vue';
 import { langs } from '@/config.js';
 import { defaultStore } from '@/store.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -368,7 +369,7 @@ function removeEmojiIndex(lang: string) {
 }
 
 async function setPinnedList() {
-	const lists = await os.api('users/lists/list');
+	const lists = await misskeyApi('users/lists/list');
 	const { canceled, result: list } = await os.select({
 		title: i18n.ts.selectList,
 		items: lists.map(x => ({
diff --git a/packages/frontend/src/pages/settings/import-export.vue b/packages/frontend/src/pages/settings/import-export.vue
index 858983a214..990eff99c1 100644
--- a/packages/frontend/src/pages/settings/import-export.vue
+++ b/packages/frontend/src/pages/settings/import-export.vue
@@ -117,11 +117,12 @@ import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { selectFile } from '@/scripts/select-file.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { $i } from '@/account.js';
-import { defaultStore } from "@/store.js";
+import { defaultStore } from '@/store.js';
 
 const excludeMutingUsers = ref(false);
 const excludeInactiveUsers = ref(false);
@@ -149,15 +150,15 @@ const onError = (ev) => {
 };
 
 const exportNotes = () => {
-	os.api('i/export-notes', {}).then(onExportSuccess).catch(onError);
+	misskeyApi('i/export-notes', {}).then(onExportSuccess).catch(onError);
 };
 
 const exportFavorites = () => {
-	os.api('i/export-favorites', {}).then(onExportSuccess).catch(onError);
+	misskeyApi('i/export-favorites', {}).then(onExportSuccess).catch(onError);
 };
 
 const exportFollowing = () => {
-	os.api('i/export-following', {
+	misskeyApi('i/export-following', {
 		excludeMuting: excludeMutingUsers.value,
 		excludeInactive: excludeInactiveUsers.value,
 	})
@@ -165,24 +166,24 @@ const exportFollowing = () => {
 };
 
 const exportBlocking = () => {
-	os.api('i/export-blocking', {}).then(onExportSuccess).catch(onError);
+	misskeyApi('i/export-blocking', {}).then(onExportSuccess).catch(onError);
 };
 
 const exportUserLists = () => {
-	os.api('i/export-user-lists', {}).then(onExportSuccess).catch(onError);
+	misskeyApi('i/export-user-lists', {}).then(onExportSuccess).catch(onError);
 };
 
 const exportMuting = () => {
-	os.api('i/export-mute', {}).then(onExportSuccess).catch(onError);
+	misskeyApi('i/export-mute', {}).then(onExportSuccess).catch(onError);
 };
 
 const exportAntennas = () => {
-	os.api('i/export-antennas', {}).then(onExportSuccess).catch(onError);
+	misskeyApi('i/export-antennas', {}).then(onExportSuccess).catch(onError);
 };
 
 const importFollowing = async (ev) => {
 	const file = await selectFile(ev.currentTarget ?? ev.target);
-	os.api('i/import-following', {
+	misskeyApi('i/import-following', {
 		fileId: file.id,
 		withReplies: withReplies.value,
 	}).then(onImportSuccess).catch(onError);
@@ -190,22 +191,22 @@ const importFollowing = async (ev) => {
 
 const importUserLists = async (ev) => {
 	const file = await selectFile(ev.currentTarget ?? ev.target);
-	os.api('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
+	misskeyApi('i/import-user-lists', { fileId: file.id }).then(onImportSuccess).catch(onError);
 };
 
 const importMuting = async (ev) => {
 	const file = await selectFile(ev.currentTarget ?? ev.target);
-	os.api('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
+	misskeyApi('i/import-muting', { fileId: file.id }).then(onImportSuccess).catch(onError);
 };
 
 const importBlocking = async (ev) => {
 	const file = await selectFile(ev.currentTarget ?? ev.target);
-	os.api('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
+	misskeyApi('i/import-blocking', { fileId: file.id }).then(onImportSuccess).catch(onError);
 };
 
 const importAntennas = async (ev) => {
 	const file = await selectFile(ev.currentTarget ?? ev.target);
-	os.api('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError);
+	misskeyApi('i/import-antennas', { fileId: file.id }).then(onImportSuccess).catch(onError);
 };
 
 const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/migration.vue b/packages/frontend/src/pages/settings/migration.vue
index 15bf4691b2..2699f0ad63 100644
--- a/packages/frontend/src/pages/settings/migration.vue
+++ b/packages/frontend/src/pages/settings/migration.vue
@@ -27,7 +27,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 		</div>
 	</MkFolder>
 
-	<MkFolder :defaultOpen="!!$i?.movedTo">
+	<MkFolder :defaultOpen="!!$i.movedTo">
 		<template #icon><i class="ti ti-plane-departure"></i></template>
 		<template #label>{{ i18n.ts._accountMigration.moveTo }}</template>
 
@@ -66,24 +66,27 @@ import MkButton from '@/components/MkButton.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkUserInfo from '@/components/MkUserInfo.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 
+const $i = signinRequired();
+
 const moveToAccount = ref('');
 const movedTo = ref<Misskey.entities.UserDetailed>();
 const accountAliases = ref(['']);
 
 async function init() {
-	if ($i?.movedTo) {
-		movedTo.value = await os.api('users/show', { userId: $i.movedTo });
+	if ($i.movedTo) {
+		movedTo.value = await misskeyApi('users/show', { userId: $i.movedTo });
 	} else {
 		moveToAccount.value = '';
 	}
 
-	if ($i?.alsoKnownAs && $i.alsoKnownAs.length > 0) {
-		const alsoKnownAs = await os.api('users/show', { userIds: $i.alsoKnownAs });
+	if ($i.alsoKnownAs && $i.alsoKnownAs.length > 0) {
+		const alsoKnownAs = await misskeyApi('users/show', { userIds: $i.alsoKnownAs });
 		accountAliases.value = (alsoKnownAs && alsoKnownAs.length > 0) ? alsoKnownAs.map(user => `@${Misskey.acct.toString(user)}`) : [''];
 	} else {
 		accountAliases.value = [''];
diff --git a/packages/frontend/src/pages/settings/mute-block.instance-mute.vue b/packages/frontend/src/pages/settings/mute-block.instance-mute.vue
index 4b5080ea8f..f986be41c3 100644
--- a/packages/frontend/src/pages/settings/mute-block.instance-mute.vue
+++ b/packages/frontend/src/pages/settings/mute-block.instance-mute.vue
@@ -19,11 +19,13 @@ import { ref, watch } from 'vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
-const instanceMutes = ref($i!.mutedInstances.join('\n'));
+const $i = signinRequired();
+
+const instanceMutes = ref($i.mutedInstances.join('\n'));
 const changed = ref(false);
 
 async function save() {
@@ -32,7 +34,7 @@ async function save() {
 		.map(el => el.trim())
 		.filter(el => el);
 
-	await os.api('i/update', {
+	await misskeyApi('i/update', {
 		mutedInstances: mutes,
 	});
 
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 83f7baf428..bc31025e59 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -9,14 +9,14 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<template #icon><i class="ti ti-message-off"></i></template>
 		<template #label>{{ i18n.ts.wordMute }}</template>
 
-		<XWordMute :muted="$i!.mutedWords" @save="saveMutedWords"/>
+		<XWordMute :muted="$i.mutedWords" @save="saveMutedWords"/>
 	</MkFolder>
 
 	<MkFolder>
 		<template #icon><i class="ti ti-message-off"></i></template>
 		<template #label>{{ i18n.ts.hardWordMute }}</template>
 
-		<XWordMute :muted="$i!.hardMutedWords" @save="saveHardMutedWords"/>
+		<XWordMute :muted="$i.hardMutedWords" @save="saveHardMutedWords"/>
 	</MkFolder>
 
 	<MkFolder>
@@ -135,10 +135,13 @@ import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkUserCardMini from '@/components/MkUserCardMini.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { infoImageUrl } from '@/instance.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import MkFolder from '@/components/MkFolder.vue';
 
+const $i = signinRequired();
+
 const renoteMutingPagination = {
 	endpoint: 'renote-mute/list' as const,
 	limit: 10,
@@ -216,11 +219,11 @@ async function toggleBlockItem(item) {
 }
 
 async function saveMutedWords(mutedWords: (string | string[])[]) {
-	await os.api('i/update', { mutedWords });
+	await misskeyApi('i/update', { mutedWords });
 }
 
 async function saveHardMutedWords(hardMutedWords: (string | string[])[]) {
-	await os.api('i/update', { hardMutedWords });
+	await misskeyApi('i/update', { hardMutedWords });
 }
 
 const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/notifications.vue b/packages/frontend/src/pages/settings/notifications.vue
index 98b82f7116..766f33ff65 100644
--- a/packages/frontend/src/pages/settings/notifications.vue
+++ b/packages/frontend/src/pages/settings/notifications.vue
@@ -62,18 +62,21 @@ import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import * as os from '@/os.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue';
 import { notificationTypes } from '@/const.js';
 
+const $i = signinRequired();
+
 const nonConfigurableNotificationTypes = ['note', 'roleAssigned', 'followRequestAccepted', 'achievementEarned'];
 
 const allowButton = shallowRef<InstanceType<typeof MkPushNotificationAllowButton>>();
 const pushRegistrationInServer = computed(() => allowButton.value?.pushRegistrationInServer);
 const sendReadMessage = computed(() => pushRegistrationInServer.value?.sendReadMessage || false);
-const userLists = await os.api('users/lists/list');
+const userLists = await misskeyApi('users/lists/list');
 
 async function readAllUnreadNotes() {
 	await os.apiWithDialog('i/read-all-unread-notes');
@@ -86,11 +89,11 @@ async function readAllNotifications() {
 async function updateReceiveConfig(type, value) {
 	await os.apiWithDialog('i/update', {
 		notificationRecieveConfig: {
-			...$i!.notificationRecieveConfig,
+			...$i.notificationRecieveConfig,
 			[type]: value,
 		},
 	}).then(i => {
-		$i!.notificationRecieveConfig = i.notificationRecieveConfig;
+		$i.notificationRecieveConfig = i.notificationRecieveConfig;
 	});
 }
 
@@ -107,7 +110,7 @@ function onChangeSendReadMessage(v: boolean) {
 }
 
 function testNotification(): void {
-	os.api('notifications/test-notification');
+	misskeyApi('notifications/test-notification');
 }
 
 const headerActions = computed(() => []);
diff --git a/packages/frontend/src/pages/settings/other.vue b/packages/frontend/src/pages/settings/other.vue
index 340a9550b4..c4367da1aa 100644
--- a/packages/frontend/src/pages/settings/other.vue
+++ b/packages/frontend/src/pages/settings/other.vue
@@ -93,26 +93,21 @@ import FormInfo from '@/components/MkInfo.vue';
 import MkKeyValue from '@/components/MkKeyValue.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
-import { signout, $i } from '@/account.js';
+import { signout, signinRequired } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import FormSection from '@/components/form/section.vue';
 
+const $i = signinRequired();
+
 const reportError = computed(defaultStore.makeGetterSetter('reportError'));
 const enableCondensedLineForAcct = computed(defaultStore.makeGetterSetter('enableCondensedLineForAcct'));
 const devMode = computed(defaultStore.makeGetterSetter('devMode'));
 const defaultWithReplies = computed(defaultStore.makeGetterSetter('defaultWithReplies'));
 
-function onChangeInjectFeaturedNote(v) {
-	os.api('i/update', {
-		injectFeaturedNote: v,
-	}).then((i) => {
-		$i!.injectFeaturedNote = i.injectFeaturedNote;
-	});
-}
-
 async function deleteAccount() {
 	{
 		const { canceled } = await os.confirm({
@@ -154,7 +149,7 @@ async function updateRepliesAll(withReplies: boolean) {
 	});
 	if (canceled) return;
 
-	os.api('following/update-all', { withReplies });
+	misskeyApi('following/update-all', { withReplies });
 }
 
 watch([
diff --git a/packages/frontend/src/pages/settings/preferences-backups.vue b/packages/frontend/src/pages/settings/preferences-backups.vue
index cc6223218f..ea9c5949ab 100644
--- a/packages/frontend/src/pages/settings/preferences-backups.vue
+++ b/packages/frontend/src/pages/settings/preferences-backups.vue
@@ -43,6 +43,7 @@ import FormSection from '@/components/form/section.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkInfo from '@/components/MkInfo.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { ColdDeviceStorage, defaultStore } from '@/store.js';
 import { unisonReload } from '@/scripts/unison-reload.js';
 import { useStream } from '@/stream.js';
@@ -144,7 +145,7 @@ const connection = $i && useStream().useChannel('main');
 
 const profiles = ref<Record<string, Profile> | null>(null);
 
-os.api('i/registry/get-all', { scope })
+misskeyApi('i/registry/get-all', { scope })
 	.then(res => {
 		profiles.value = res || {};
 	});
diff --git a/packages/frontend/src/pages/settings/privacy.vue b/packages/frontend/src/pages/settings/privacy.vue
index 67a2f2cb40..3698590d51 100644
--- a/packages/frontend/src/pages/settings/privacy.vue
+++ b/packages/frontend/src/pages/settings/privacy.vue
@@ -77,12 +77,14 @@ import MkSwitch from '@/components/MkSwitch.vue';
 import MkSelect from '@/components/MkSelect.vue';
 import FormSection from '@/components/form/section.vue';
 import MkFolder from '@/components/MkFolder.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
+const $i = signinRequired();
+
 const isLocked = ref($i.isLocked);
 const autoAcceptFollowed = ref($i.autoAcceptFollowed);
 const noCrawle = ref($i.noCrawle);
@@ -90,8 +92,8 @@ const preventAiLearning = ref($i.preventAiLearning);
 const isExplorable = ref($i.isExplorable);
 const hideOnlineStatus = ref($i.hideOnlineStatus);
 const publicReactions = ref($i.publicReactions);
-const followingVisibility = ref($i?.followingVisibility);
-const followersVisibility = ref($i?.followersVisibility);
+const followingVisibility = ref($i.followingVisibility);
+const followersVisibility = ref($i.followersVisibility);
 
 const defaultNoteVisibility = computed(defaultStore.makeGetterSetter('defaultNoteVisibility'));
 const defaultNoteLocalOnly = computed(defaultStore.makeGetterSetter('defaultNoteLocalOnly'));
@@ -99,7 +101,7 @@ const rememberNoteVisibility = computed(defaultStore.makeGetterSetter('rememberN
 const keepCw = computed(defaultStore.makeGetterSetter('keepCw'));
 
 function save() {
-	os.api('i/update', {
+	misskeyApi('i/update', {
 		isLocked: !!isLocked.value,
 		autoAcceptFollowed: !!autoAcceptFollowed.value,
 		noCrawle: !!noCrawle.value,
diff --git a/packages/frontend/src/pages/settings/profile.vue b/packages/frontend/src/pages/settings/profile.vue
index d28c8284cf..e2d98cbf29 100644
--- a/packages/frontend/src/pages/settings/profile.vue
+++ b/packages/frontend/src/pages/settings/profile.vue
@@ -120,7 +120,7 @@ import FormSlot from '@/components/form/slot.vue';
 import { selectFile } from '@/scripts/select-file.js';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import { langmap } from '@/scripts/langmap.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { claimAchievement } from '@/scripts/achievements.js';
@@ -128,6 +128,8 @@ import { defaultStore } from '@/store.js';
 import MkInfo from '@/components/MkInfo.vue';
 import MkTextarea from '@/components/MkTextarea.vue';
 
+const $i = signinRequired();
+
 const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
 
 const reactionAcceptance = computed(defaultStore.makeGetterSetter('reactionAcceptance'));
@@ -138,8 +140,8 @@ const profile = reactive({
 	location: $i.location,
 	birthday: $i.birthday,
 	lang: $i.lang,
-	isBot: $i.isBot,
-	isCat: $i.isCat,
+	isBot: $i.isBot ?? false,
+	isCat: $i.isCat ?? false,
 });
 
 watch(() => profile, () => {
@@ -148,7 +150,7 @@ watch(() => profile, () => {
 	deep: true,
 });
 
-const fields = ref($i?.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
+const fields = ref($i.fields.map(field => ({ id: Math.random().toString(), name: field.name, value: field.value })) ?? []);
 const fieldEditMode = ref(false);
 
 function addField() {
diff --git a/packages/frontend/src/pages/settings/roles.vue b/packages/frontend/src/pages/settings/roles.vue
index 40671f7132..bb37730e35 100644
--- a/packages/frontend/src/pages/settings/roles.vue
+++ b/packages/frontend/src/pages/settings/roles.vue
@@ -27,15 +27,11 @@ import { computed } from 'vue';
 import FormSection from '@/components/form/section.vue';
 import * as os from '@/os.js';
 import { i18n } from '@/i18n.js';
-import { $i } from '@/account.js';
+import { signinRequired } from '@/account.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import MkRolePreview from '@/components/MkRolePreview.vue';
 
-function save() {
-	os.apiWithDialog('i/update', {
-
-	});
-}
+const $i = signinRequired();
 
 const headerActions = computed(() => []);
 
diff --git a/packages/frontend/src/pages/settings/security.vue b/packages/frontend/src/pages/settings/security.vue
index 3f85f27e47..0787e0c3a3 100644
--- a/packages/frontend/src/pages/settings/security.vue
+++ b/packages/frontend/src/pages/settings/security.vue
@@ -47,6 +47,7 @@ import FormSlot from '@/components/form/slot.vue';
 import MkButton from '@/components/MkButton.vue';
 import MkPagination from '@/components/MkPagination.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
@@ -92,7 +93,7 @@ async function regenerateToken() {
 	const auth = await os.authenticateDialog();
 	if (auth.canceled) return;
 
-	os.api('i/regenerate-token', {
+	misskeyApi('i/regenerate-token', {
 		password: auth.result.password,
 		token: auth.result.token,
 	});
diff --git a/packages/frontend/src/pages/settings/sounds.sound.vue b/packages/frontend/src/pages/settings/sounds.sound.vue
index 2f4cd1be2c..57bafce0ac 100644
--- a/packages/frontend/src/pages/settings/sounds.sound.vue
+++ b/packages/frontend/src/pages/settings/sounds.sound.vue
@@ -32,6 +32,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkRange from '@/components/MkRange.vue';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { playFile, soundsTypes, getSoundDuration } from '@/scripts/sound.js';
 import { selectFile } from '@/scripts/select-file.js';
 
@@ -53,7 +54,7 @@ const fileName = ref<string>('');
 const volume = ref(props.volume);
 
 if (type.value === '_driveFile_' && fileId.value) {
-	const apiRes = await os.api('drive/files/show', {
+	const apiRes = await misskeyApi('drive/files/show', {
 		fileId: fileId.value,
 	});
 	fileName.value = apiRes.name;
diff --git a/packages/frontend/src/pages/settings/statusbar.vue b/packages/frontend/src/pages/settings/statusbar.vue
index 294da80bb5..b347a869f4 100644
--- a/packages/frontend/src/pages/settings/statusbar.vue
+++ b/packages/frontend/src/pages/settings/statusbar.vue
@@ -21,7 +21,7 @@ import { v4 as uuid } from 'uuid';
 import XStatusbar from './statusbar.statusbar.vue';
 import MkFolder from '@/components/MkFolder.vue';
 import MkButton from '@/components/MkButton.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -31,7 +31,7 @@ const statusbars = defaultStore.reactiveState.statusbars;
 const userLists = ref<Misskey.entities.UserList[] | null>(null);
 
 onMounted(() => {
-	os.api('users/lists/list').then(res => {
+	misskeyApi('users/lists/list').then(res => {
 		userLists.value = res;
 	});
 });
diff --git a/packages/frontend/src/pages/settings/webhook.edit.vue b/packages/frontend/src/pages/settings/webhook.edit.vue
index c1695dc6a5..9eb344bd46 100644
--- a/packages/frontend/src/pages/settings/webhook.edit.vue
+++ b/packages/frontend/src/pages/settings/webhook.edit.vue
@@ -48,6 +48,7 @@ import FormSection from '@/components/form/section.vue';
 import MkSwitch from '@/components/MkSwitch.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { useRouter } from '@/router.js';
@@ -58,7 +59,7 @@ const props = defineProps<{
 	webhookId: string;
 }>();
 
-const webhook = await os.api('i/webhooks/show', {
+const webhook = await misskeyApi('i/webhooks/show', {
 	webhookId: props.webhookId,
 });
 
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index cb5acf3afa..030cfbb905 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -37,6 +37,7 @@ import * as Misskey from 'misskey-js';
 import MkButton from '@/components/MkButton.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { postMessageToParentWindow } from '@/scripts/post-message.js';
 import { i18n } from '@/i18n.js';
@@ -76,7 +77,7 @@ async function init() {
 			]
 			// TypeScriptの指示通りに変換する
 				.map(q => 'username' in q ? { username: q.username, host: q.host === null ? undefined : q.host } : q)
-				.map(q => os.api('users/show', q)
+				.map(q => misskeyApi('users/show', q)
 					.then(user => {
 						visibleUsers.value.push(user);
 					}, () => {
@@ -91,11 +92,11 @@ async function init() {
 		const replyId = urlParams.get('replyId');
 		const replyUri = urlParams.get('replyUri');
 		if (replyId) {
-			reply.value = await os.api('notes/show', {
+			reply.value = await misskeyApi('notes/show', {
 				noteId: replyId,
 			});
 		} else if (replyUri) {
-			const obj = await os.api('ap/show', {
+			const obj = await misskeyApi('ap/show', {
 				uri: replyUri,
 			});
 			if (obj.type === 'Note') {
@@ -108,11 +109,11 @@ async function init() {
 		const renoteId = urlParams.get('renoteId');
 		const renoteUri = urlParams.get('renoteUri');
 		if (renoteId) {
-			renote.value = await os.api('notes/show', {
+			renote.value = await misskeyApi('notes/show', {
 				noteId: renoteId,
 			});
 		} else if (renoteUri) {
-			const obj = await os.api('ap/show', {
+			const obj = await misskeyApi('ap/show', {
 				uri: renoteUri,
 			});
 			if (obj.type === 'Note') {
@@ -126,7 +127,7 @@ async function init() {
 		if (fileIds) {
 			await Promise.all(
 				fileIds.split(',')
-					.map(fileId => os.api('drive/files/show', { fileId })
+					.map(fileId => misskeyApi('drive/files/show', { fileId })
 						.then(file => {
 							files.value.push(file);
 						}, () => {
diff --git a/packages/frontend/src/pages/signup-complete.vue b/packages/frontend/src/pages/signup-complete.vue
index 638c7e8773..3f007b7afc 100644
--- a/packages/frontend/src/pages/signup-complete.vue
+++ b/packages/frontend/src/pages/signup-complete.vue
@@ -31,6 +31,7 @@ import MkAnimBg from '@/components/MkAnimBg.vue';
 import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 const submitting = ref(false);
 
@@ -42,7 +43,7 @@ function submit() {
 	if (submitting.value) return;
 	submitting.value = true;
 
-	os.api('signup-pending', {
+	misskeyApi('signup-pending', {
 		code: props.code,
 	}).then(res => {
 		return login(res.i, '/');
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index 1b24f98bdb..6fe8963f51 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -40,6 +40,7 @@ import MkInfo from '@/components/MkInfo.vue';
 import MkPostForm from '@/components/MkPostForm.vue';
 import { scroll } from '@/scripts/scroll.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
@@ -122,7 +123,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
 }
 
 async function chooseChannel(ev: MouseEvent): Promise<void> {
-	const channels = await os.api('channels/my-favorites', {
+	const channels = await misskeyApi('channels/my-favorites', {
 		limit: 100,
 	});
 	const items: MenuItem[] = [
diff --git a/packages/frontend/src/pages/user-list-timeline.vue b/packages/frontend/src/pages/user-list-timeline.vue
index ba22d784c7..19c376c77b 100644
--- a/packages/frontend/src/pages/user-list-timeline.vue
+++ b/packages/frontend/src/pages/user-list-timeline.vue
@@ -28,7 +28,7 @@ import { computed, watch, ref, shallowRef } from 'vue';
 import * as Misskey from 'misskey-js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { scroll } from '@/scripts/scroll.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useRouter } from '@/router.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
@@ -45,7 +45,7 @@ const tlEl = shallowRef<InstanceType<typeof MkTimeline>>();
 const rootEl = shallowRef<HTMLElement>();
 
 watch(() => props.listId, async () => {
-	list.value = await os.api('users/lists/show', {
+	list.value = await misskeyApi('users/lists/show', {
 		listId: props.listId,
 	});
 }, { immediate: true });
diff --git a/packages/frontend/src/pages/user/activity.following.vue b/packages/frontend/src/pages/user/activity.following.vue
index bd1159cb32..798b640647 100644
--- a/packages/frontend/src/pages/user/activity.following.vue
+++ b/packages/frontend/src/pages/user/activity.following.vue
@@ -18,7 +18,7 @@ import { onMounted, shallowRef, ref } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
@@ -61,7 +61,7 @@ async function renderChart() {
 		}));
 	};
 
-	const raw = await os.api('charts/user/following', { userId: props.user.id, limit: chartLimit, span: 'day' });
+	const raw = await misskeyApi('charts/user/following', { userId: props.user.id, limit: chartLimit, span: 'day' });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 
diff --git a/packages/frontend/src/pages/user/activity.heatmap.vue b/packages/frontend/src/pages/user/activity.heatmap.vue
index ff46db9653..ea3276a890 100644
--- a/packages/frontend/src/pages/user/activity.heatmap.vue
+++ b/packages/frontend/src/pages/user/activity.heatmap.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { onMounted, nextTick, watch, shallowRef, ref } from 'vue';
 import { Chart } from 'chart.js';
 import * as Misskey from 'misskey-js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { alpha } from '@/scripts/color.js';
@@ -74,7 +74,7 @@ async function renderChart() {
 	let values;
 
 	if (props.src === 'notes') {
-		const raw = await os.api('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
+		const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
 		values = raw.inc;
 	}
 
diff --git a/packages/frontend/src/pages/user/activity.notes.vue b/packages/frontend/src/pages/user/activity.notes.vue
index dd035641d8..a55d98d989 100644
--- a/packages/frontend/src/pages/user/activity.notes.vue
+++ b/packages/frontend/src/pages/user/activity.notes.vue
@@ -18,7 +18,7 @@ import { onMounted, shallowRef, ref } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
@@ -61,7 +61,7 @@ async function renderChart() {
 		}));
 	};
 
-	const raw = await os.api('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
+	const raw = await misskeyApi('charts/user/notes', { userId: props.user.id, limit: chartLimit, span: 'day' });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 
diff --git a/packages/frontend/src/pages/user/activity.pv.vue b/packages/frontend/src/pages/user/activity.pv.vue
index 2dd9a1570f..fe9acd322c 100644
--- a/packages/frontend/src/pages/user/activity.pv.vue
+++ b/packages/frontend/src/pages/user/activity.pv.vue
@@ -18,7 +18,7 @@ import { onMounted, shallowRef, ref } from 'vue';
 import { Chart, ChartDataset } from 'chart.js';
 import * as Misskey from 'misskey-js';
 import gradient from 'chartjs-plugin-gradient';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore } from '@/store.js';
 import { useChartTooltip } from '@/scripts/use-chart-tooltip.js';
 import { chartVLine } from '@/scripts/chart-vline.js';
@@ -61,7 +61,7 @@ async function renderChart() {
 		}));
 	};
 
-	const raw = await os.api('charts/user/pv', { userId: props.user.id, limit: chartLimit, span: 'day' });
+	const raw = await misskeyApi('charts/user/pv', { userId: props.user.id, limit: chartLimit, span: 'day' });
 
 	const vLineColor = defaultStore.state.darkMode ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
 
diff --git a/packages/frontend/src/pages/user/followers.vue b/packages/frontend/src/pages/user/followers.vue
index a4d516a1de..23c7fc0494 100644
--- a/packages/frontend/src/pages/user/followers.vue
+++ b/packages/frontend/src/pages/user/followers.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XFollowList from './follow-list.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 
@@ -37,7 +37,7 @@ const error = ref<any>(null);
 function fetchUser(): void {
 	if (props.acct == null) return;
 	user.value = null;
-	os.api('users/show', Misskey.acct.parse(props.acct)).then(u => {
+	misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => {
 		user.value = u;
 	}).catch(err => {
 		error.value = err;
diff --git a/packages/frontend/src/pages/user/following.vue b/packages/frontend/src/pages/user/following.vue
index 99cb098d65..d1dc935884 100644
--- a/packages/frontend/src/pages/user/following.vue
+++ b/packages/frontend/src/pages/user/following.vue
@@ -22,7 +22,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import XFollowList from './follow-list.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 
@@ -37,7 +37,7 @@ const error = ref<any>(null);
 function fetchUser(): void {
 	if (props.acct == null) return;
 	user.value = null;
-	os.api('users/show', Misskey.acct.parse(props.acct)).then(u => {
+	misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => {
 		user.value = u;
 	}).catch(err => {
 		error.value = err;
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index 2a9eb5f8e4..5258165d7c 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -171,7 +171,7 @@ import { i18n } from '@/i18n.js';
 import { $i, iAmModerator } from '@/account.js';
 import { dateString } from '@/filters/date.js';
 import { confetti } from '@/scripts/confetti.js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js';
 
 function calcAge(birthdate: string): number {
@@ -215,7 +215,7 @@ const moderationNote = ref(props.user.moderationNote);
 const editModerationNote = ref(false);
 
 watch(moderationNote, async () => {
-	await os.api('admin/update-user-note', { userId: props.user.id, text: moderationNote.value });
+	await misskeyApi('admin/update-user-note', { userId: props.user.id, text: moderationNote.value });
 });
 
 const style = computed(() => {
@@ -266,7 +266,7 @@ function adjustMemoTextarea() {
 }
 
 async function updateMemo() {
-	await api('users/update-memo', {
+	await misskeyApi('users/update-memo', {
 		memo: memoDraft.value,
 		userId: props.user.id,
 	});
diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue
index 32561e6b0b..506d324683 100644
--- a/packages/frontend/src/pages/user/index.files.vue
+++ b/packages/frontend/src/pages/user/index.files.vue
@@ -37,7 +37,7 @@ import { onMounted, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
 import { notePage } from '@/filters/note.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkContainer from '@/components/MkContainer.vue';
 import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
 import { defaultStore } from '@/store.js';
@@ -61,7 +61,7 @@ function thumbnail(image: Misskey.entities.DriveFile): string {
 }
 
 onMounted(() => {
-	os.api('users/notes', {
+	misskeyApi('users/notes', {
 		userId: props.user.id,
 		withFiles: true,
 		limit: 15,
diff --git a/packages/frontend/src/pages/user/index.vue b/packages/frontend/src/pages/user/index.vue
index 1e9a860974..95869e7b8c 100644
--- a/packages/frontend/src/pages/user/index.vue
+++ b/packages/frontend/src/pages/user/index.vue
@@ -32,7 +32,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { defineAsyncComponent, computed, watch, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { acct as getAcct } from '@/filters/user.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 import { i18n } from '@/i18n.js';
 import { $i } from '@/account.js';
@@ -63,7 +63,7 @@ const error = ref<any>(null);
 function fetchUser(): void {
 	if (props.acct == null) return;
 	user.value = null;
-	os.api('users/show', Misskey.acct.parse(props.acct)).then(u => {
+	misskeyApi('users/show', Misskey.acct.parse(props.acct)).then(u => {
 		user.value = u;
 	}).catch(err => {
 		error.value = err;
diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue
index 3ad34355f5..2bb9746bd6 100644
--- a/packages/frontend/src/pages/welcome.entrance.a.vue
+++ b/packages/frontend/src/pages/welcome.entrance.a.vue
@@ -39,7 +39,7 @@ import XTimeline from './welcome.timeline.vue';
 import MarqueeText from '@/components/MkMarquee.vue';
 import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue';
 import misskeysvg from '/client-assets/misskey.svg';
-import * as os from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue';
 import { getProxiedImageUrl } from '@/scripts/media-proxy.js';
 
@@ -53,11 +53,11 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string
 	return getProxiedImageUrl(instance.iconUrl, 'preview');
 }
 
-os.api('meta', { detail: true }).then(_meta => {
+misskeyApi('meta', { detail: true }).then(_meta => {
 	meta.value = _meta;
 });
 
-os.apiGet('federation/instances', {
+misskeyApiGet('federation/instances', {
 	sort: '+pubSub',
 	limit: 20,
 }).then(_instances => {
diff --git a/packages/frontend/src/pages/welcome.setup.vue b/packages/frontend/src/pages/welcome.setup.vue
index 61b86f993d..40187b861e 100644
--- a/packages/frontend/src/pages/welcome.setup.vue
+++ b/packages/frontend/src/pages/welcome.setup.vue
@@ -40,6 +40,7 @@ import MkButton from '@/components/MkButton.vue';
 import MkInput from '@/components/MkInput.vue';
 import { host, version } from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { login } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import MkAnimBg from '@/components/MkAnimBg.vue';
@@ -52,7 +53,7 @@ function submit() {
 	if (submitting.value) return;
 	submitting.value = true;
 
-	os.api('admin/accounts/create', {
+	misskeyApi('admin/accounts/create', {
 		username: username.value,
 		password: password.value,
 	}).then(res => {
diff --git a/packages/frontend/src/pages/welcome.timeline.vue b/packages/frontend/src/pages/welcome.timeline.vue
index 92be80228a..07c98571e4 100644
--- a/packages/frontend/src/pages/welcome.timeline.vue
+++ b/packages/frontend/src/pages/welcome.timeline.vue
@@ -32,14 +32,14 @@ import { onUpdated, ref, shallowRef } from 'vue';
 import MkReactionsViewer from '@/components/MkReactionsViewer.vue';
 import MkMediaList from '@/components/MkMediaList.vue';
 import MkPoll from '@/components/MkPoll.vue';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { getScrollContainer } from '@/scripts/scroll.js';
 
 const notes = ref<Misskey.entities.Note[]>([]);
 const isScrolling = ref(false);
 const scrollEl = shallowRef<HTMLElement>();
 
-os.apiGet('notes/featured').then(_notes => {
+misskeyApiGet('notes/featured').then(_notes => {
 	notes.value = _notes;
 });
 
diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue
index 7f0af1b83e..225ab91514 100644
--- a/packages/frontend/src/pages/welcome.vue
+++ b/packages/frontend/src/pages/welcome.vue
@@ -16,12 +16,12 @@ import * as Misskey from 'misskey-js';
 import XSetup from './welcome.setup.vue';
 import XEntrance from './welcome.entrance.a.vue';
 import { instanceName } from '@/config.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { definePageMetadata } from '@/scripts/page-metadata.js';
 
 const meta = ref<Misskey.entities.MetaResponse | null>(null);
 
-os.api('meta', { detail: true }).then(res => {
+misskeyApi('meta', { detail: true }).then(res => {
 	meta.value = res;
 });
 
diff --git a/packages/frontend/src/pizzax.ts b/packages/frontend/src/pizzax.ts
index d26899dbde..8723110b08 100644
--- a/packages/frontend/src/pizzax.ts
+++ b/packages/frontend/src/pizzax.ts
@@ -8,7 +8,7 @@
 import { onUnmounted, Ref, ref, watch } from 'vue';
 import { BroadcastChannel } from 'broadcast-channel';
 import { $i } from '@/account.js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { get, set } from '@/scripts/idb-proxy.js';
 import { defaultStore } from '@/store.js';
 import { useStream } from '@/stream.js';
@@ -134,7 +134,7 @@ export class Storage<T extends StateDef> {
 				window.setTimeout(async () => {
 					await defaultStore.ready;
 
-					api('i/registry/get-all', { scope: ['client', this.key] })
+					misskeyApi('i/registry/get-all', { scope: ['client', this.key] })
 						.then(kvs => {
 							const cache: Partial<T> = {};
 							for (const [k, v] of Object.entries(this.def) as [keyof T, T[keyof T]['default']][]) {
@@ -199,7 +199,7 @@ export class Storage<T extends StateDef> {
 					const cache = await get(this.registryCacheKeyName) || {};
 					cache[key] = rawValue;
 					await set(this.registryCacheKeyName, cache);
-					await api('i/registry/set', {
+					await misskeyApi('i/registry/set', {
 						scope: ['client', this.key],
 						key: key.toString(),
 						value: rawValue,
@@ -225,7 +225,10 @@ export class Storage<T extends StateDef> {
 	 * 特定のキーの、簡易的なgetter/setterを作ります
 	 * 主にvue場で設定コントロールのmodelとして使う用
 	 */
-	public makeGetterSetter<K extends keyof T>(key: K, getter?: (v: T[K]) => unknown, setter?: (v: unknown) => T[K]) {
+	public makeGetterSetter<K extends keyof T>(key: K, getter?: (v: T[K]) => unknown, setter?: (v: unknown) => T[K]): {
+		get: () => T[K]['default'];
+		set: (value: T[K]['default']) => void;
+	} {
 		const valueRef = ref(this.state[key]);
 
 		const stop = watch(this.reactiveState[key], val => {
diff --git a/packages/frontend/src/plugin.ts b/packages/frontend/src/plugin.ts
index 5e49af4858..acc3e836fb 100644
--- a/packages/frontend/src/plugin.ts
+++ b/packages/frontend/src/plugin.ts
@@ -4,7 +4,7 @@
  */
 
 import { Interpreter, Parser, utils, values } from '@syuilo/aiscript';
-import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import { inputText } from '@/os.js';
 import { Plugin, noteActions, notePostInterruptors, noteViewInterruptors, postFormActions, userActions, pageViewInterruptors } from '@/store.js';
 
@@ -19,19 +19,7 @@ export async function install(plugin: Plugin): Promise<void> {
 		plugin: plugin,
 		storageKey: 'plugins:' + plugin.id,
 	}), {
-		in: (q): Promise<string> => {
-			return new Promise(ok => {
-				inputText({
-					title: q,
-				}).then(({ canceled, result: a }) => {
-					if (canceled) {
-						ok('');
-					} else {
-						ok(a);
-					}
-				});
-			});
-		},
+		in: aiScriptReadline,
 		out: (value): void => {
 			console.log(value);
 		},
diff --git a/packages/frontend/src/scripts/achievements.ts b/packages/frontend/src/scripts/achievements.ts
index e7585fcf81..4b6b044d8b 100644
--- a/packages/frontend/src/scripts/achievements.ts
+++ b/packages/frontend/src/scripts/achievements.ts
@@ -3,7 +3,7 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i } from '@/account.js';
 
 export const ACHIEVEMENT_TYPES = [
@@ -489,7 +489,7 @@ export async function claimAchievement(type: typeof ACHIEVEMENT_TYPES[number]) {
 	window.setTimeout(() => {
 		claimingQueue.delete(type);
 	}, 500);
-	os.api('i/claim-achievement', { name: type });
+	misskeyApi('i/claim-achievement', { name: type });
 }
 
 if (_DEV_) {
diff --git a/packages/frontend/src/scripts/aiscript/api.ts b/packages/frontend/src/scripts/aiscript/api.ts
index 038ae23109..c13849cc8f 100644
--- a/packages/frontend/src/scripts/aiscript/api.ts
+++ b/packages/frontend/src/scripts/aiscript/api.ts
@@ -5,12 +5,23 @@
 
 import { utils, values } from '@syuilo/aiscript';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i } from '@/account.js';
 import { miLocalStorage } from '@/local-storage.js';
 import { customEmojis } from '@/custom-emojis.js';
 import { url, lang } from '@/config.js';
 import { nyaize } from '@/scripts/nyaize.js';
 
+export function aiScriptReadline(q: string): Promise<string> {
+	return new Promise(ok => {
+		os.inputText({
+			title: q,
+		}).then(({ result: a }) => {
+			ok(a ?? '');
+		});
+	});
+}
+
 export function createAiScriptEnv(opts) {
 	return {
 		USER_ID: $i ? values.STR($i.id) : values.NULL,
@@ -44,7 +55,7 @@ export function createAiScriptEnv(opts) {
 				if (typeof token.value !== 'string') throw new Error('invalid token');
 			}
 			const actualToken: string|null = token?.value ?? opts.token ?? null;
-			return os.api(ep.value, utils.valToJs(param), actualToken).then(res => {
+			return misskeyApi(ep.value, utils.valToJs(param), actualToken).then(res => {
 				return utils.jsToVal(res);
 			}, err => {
 				return values.ERROR('request_failed', utils.jsToVal(err));
diff --git a/packages/frontend/src/scripts/clicker-game.ts b/packages/frontend/src/scripts/clicker-game.ts
index 5ad076e5ef..360bea903c 100644
--- a/packages/frontend/src/scripts/clicker-game.ts
+++ b/packages/frontend/src/scripts/clicker-game.ts
@@ -4,7 +4,7 @@
  */
 
 import { ref, computed } from 'vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 type SaveData = {
 	gameVersion: number;
@@ -23,7 +23,7 @@ let prev = '';
 
 export async function load() {
 	try {
-		saveData.value = await os.api('i/registry/get', {
+		saveData.value = await misskeyApi('i/registry/get', {
 			scope: ['clickerGame'],
 			key: 'saveData',
 		});
@@ -63,7 +63,7 @@ export async function save() {
 	const current = JSON.stringify(saveData.value);
 	if (current === prev) return;
 
-	await os.api('i/registry/set', {
+	await misskeyApi('i/registry/set', {
 		scope: ['clickerGame'],
 		key: 'saveData',
 		value: saveData.value,
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index 222fd9b0b7..f7e0369419 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -3,7 +3,11 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-type EnumItem = string | {label: string; value: string;};
+type EnumItem = string | {
+	label: string;
+	value: string;
+};
+
 export type FormItem = {
 	label?: string;
 	type: 'string';
@@ -36,16 +40,23 @@ export type FormItem = {
 		label: string;
 		value: unknown;
 	}[];
+} | {
+	label?: string;
+	type: 'range';
+	default: number | null;
+	step: number;
+	min: number;
+	max: number;
 } | {
 	label?: string;
 	type: 'object';
 	default: Record<string, unknown> | null;
-	hidden: true;
+	hidden: boolean;
 } | {
 	label?: string;
 	type: 'array';
 	default: unknown[] | null;
-	hidden: true;
+	hidden: boolean;
 };
 
 export type Form = Record<string, FormItem>;
@@ -55,6 +66,7 @@ type GetItemType<Item extends FormItem> =
 	Item['type'] extends 'number' ? number :
 	Item['type'] extends 'boolean' ? boolean :
 	Item['type'] extends 'radio' ? unknown :
+	Item['type'] extends 'range' ? number :
 	Item['type'] extends 'enum' ? string :
 	Item['type'] extends 'array' ? unknown[] :
 	Item['type'] extends 'object' ? Record<string, unknown>
diff --git a/packages/frontend/src/scripts/gen-search-query.ts b/packages/frontend/src/scripts/gen-search-query.ts
index 54654980f2..068cd9cd93 100644
--- a/packages/frontend/src/scripts/gen-search-query.ts
+++ b/packages/frontend/src/scripts/gen-search-query.ts
@@ -18,7 +18,7 @@ export async function genSearchQuery(v: any, q: string) {
 					host = at;
 				}
 			} else {
-				const user = await v.os.api('users/show', Misskey.acct.parse(at)).catch(x => null);
+				const user = await v.api('users/show', Misskey.acct.parse(at)).catch(x => null);
 				if (user) {
 					userId = user.id;
 				} else {
diff --git a/packages/frontend/src/scripts/get-drive-file-menu.ts b/packages/frontend/src/scripts/get-drive-file-menu.ts
index f8496f0711..59c46c2cbc 100644
--- a/packages/frontend/src/scripts/get-drive-file-menu.ts
+++ b/packages/frontend/src/scripts/get-drive-file-menu.ts
@@ -8,6 +8,7 @@ import { defineAsyncComponent } from 'vue';
 import { i18n } from '@/i18n.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { MenuItem } from '@/types/menu.js';
 import { defaultStore } from '@/store.js';
 
@@ -18,7 +19,7 @@ function rename(file: Misskey.entities.DriveFile) {
 		default: file.name,
 	}).then(({ canceled, result: name }) => {
 		if (canceled) return;
-		os.api('drive/files/update', {
+		misskeyApi('drive/files/update', {
 			fileId: file.id,
 			name: name,
 		});
@@ -31,7 +32,7 @@ function describe(file: Misskey.entities.DriveFile) {
 		file: file,
 	}, {
 		done: caption => {
-			os.api('drive/files/update', {
+			misskeyApi('drive/files/update', {
 				fileId: file.id,
 				comment: caption.length === 0 ? null : caption,
 			});
@@ -40,7 +41,7 @@ function describe(file: Misskey.entities.DriveFile) {
 }
 
 function toggleSensitive(file: Misskey.entities.DriveFile) {
-	os.api('drive/files/update', {
+	misskeyApi('drive/files/update', {
 		fileId: file.id,
 		isSensitive: !file.isSensitive,
 	}).catch(err => {
@@ -69,7 +70,7 @@ async function deleteFile(file: Misskey.entities.DriveFile) {
 	});
 
 	if (canceled) return;
-	os.api('drive/files/delete', {
+	misskeyApi('drive/files/delete', {
 		fileId: file.id,
 	});
 }
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index 7130e69279..110be244cb 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -10,6 +10,7 @@ import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 import { instance } from '@/instance.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { url } from '@/config.js';
 import { defaultStore, noteActions } from '@/store.js';
@@ -40,7 +41,7 @@ export async function getNoteClipMenu(props: {
 		action: () => {
 			claimAchievement('noteClipped1');
 			os.promiseDialog(
-				os.api('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
+				misskeyApi('clips/add-note', { clipId: clip.id, noteId: appearNote.id }),
 				null,
 				async (err) => {
 					if (err.id === '734806c4-542c-463a-9311-15c512803965') {
@@ -145,7 +146,7 @@ export function getNoteMenu(props: {
 		}).then(({ canceled }) => {
 			if (canceled) return;
 
-			os.api('notes/delete', {
+			misskeyApi('notes/delete', {
 				noteId: appearNote.id,
 			});
 
@@ -162,7 +163,7 @@ export function getNoteMenu(props: {
 		}).then(({ canceled }) => {
 			if (canceled) return;
 
-			os.api('notes/delete', {
+			misskeyApi('notes/delete', {
 				noteId: appearNote.id,
 			});
 
@@ -243,7 +244,7 @@ export function getNoteMenu(props: {
 	async function translate(): Promise<void> {
 		if (props.translation.value != null) return;
 		props.translating.value = true;
-		const res = await os.api('notes/translate', {
+		const res = await misskeyApi('notes/translate', {
 			noteId: appearNote.id,
 			targetLang: miLocalStorage.getItem('lang') ?? navigator.language,
 		});
@@ -253,7 +254,7 @@ export function getNoteMenu(props: {
 
 	let menu: MenuItem[];
 	if ($i) {
-		const statePromise = os.api('notes/state', {
+		const statePromise = misskeyApi('notes/state', {
 			noteId: appearNote.id,
 		});
 
@@ -330,7 +331,7 @@ export function getNoteMenu(props: {
 				icon: 'ti ti-user',
 				text: i18n.ts.user,
 				children: async () => {
-					const user = appearNote.userId === $i?.id ? $i : await os.api('users/show', { userId: appearNote.userId });
+					const user = appearNote.userId === $i?.id ? $i : await misskeyApi('users/show', { userId: appearNote.userId });
 					const { menu, cleanup } = getUserMenu(user);
 					cleanups.push(cleanup);
 					return menu;
@@ -352,6 +353,42 @@ export function getNoteMenu(props: {
 			]
 			: []
 			),
+			...(appearNote.channel && (appearNote.channel.userId === $i.id || $i.isModerator || $i.isAdmin) ? [
+				{ type: 'divider' },
+				{
+					type: 'parent' as const,
+					icon: 'ti ti-device-tv',
+					text: i18n.ts.channel,
+					children: async () => {
+						const channelChildMenu = [] as MenuItem[];
+
+						const channel = await misskeyApi('channels/show', { channelId: appearNote.channel!.id });
+
+						if (channel.pinnedNoteIds.includes(appearNote.id)) {
+							channelChildMenu.push({
+								icon: 'ti ti-pinned-off',
+								text: i18n.ts.unpin,
+								action: () => os.apiWithDialog('channels/update', {
+									channelId: appearNote.channel!.id,
+									pinnedNoteIds: channel.pinnedNoteIds.filter(id => id !== appearNote.id),
+								}),
+							});
+						} else {
+							channelChildMenu.push({
+								icon: 'ti ti-pin',
+								text: i18n.ts.pin,
+								action: () => os.apiWithDialog('channels/update', {
+									channelId: appearNote.channel!.id,
+									pinnedNoteIds: [...channel.pinnedNoteIds, appearNote.id],
+								}),
+							});
+						}
+						return channelChildMenu;
+					},
+				},
+			]
+			: []
+			),
 			...(appearNote.userId === $i.id || $i.isModerator || $i.isAdmin ? [
 				{ type: 'divider' },
 				appearNote.userId === $i.id ? {
@@ -389,7 +426,7 @@ export function getNoteMenu(props: {
 	}
 
 	if (noteActions.length > 0) {
-		menu = menu.concat([{ type: "divider" }, ...noteActions.map(action => ({
+		menu = menu.concat([{ type: 'divider' }, ...noteActions.map(action => ({
 			icon: 'ti ti-plug',
 			text: action.title,
 			action: () => {
@@ -399,7 +436,7 @@ export function getNoteMenu(props: {
 	}
 
 	if (defaultStore.state.devMode) {
-		menu = menu.concat([{ type: "divider" }, {
+		menu = menu.concat([{ type: 'divider' }, {
 			icon: 'ti ti-id',
 			text: i18n.ts.copyNoteId,
 			action: () => {
@@ -463,7 +500,7 @@ export function getRenoteMenu(props: {
 				}
 
 				if (!props.mock) {
-					os.api('notes/create', {
+					misskeyApi('notes/create', {
 						renoteId: appearNote.id,
 						channelId: appearNote.channelId,
 					}).then(() => {
@@ -508,7 +545,7 @@ export function getRenoteMenu(props: {
 				}
 
 				if (!props.mock) {
-					os.api('notes/create', {
+					misskeyApi('notes/create', {
 						localOnly,
 						visibility,
 						renoteId: appearNote.id,
diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts
index 6e5c689d97..2735253b36 100644
--- a/packages/frontend/src/scripts/get-user-menu.ts
+++ b/packages/frontend/src/scripts/get-user-menu.ts
@@ -10,6 +10,7 @@ import { i18n } from '@/i18n.js';
 import copyToClipboard from '@/scripts/copy-to-clipboard.js';
 import { host, url } from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { defaultStore, userActions } from '@/store.js';
 import { $i, iAmModerator } from '@/account.js';
 import { mainRouter } from '@/router.js';
@@ -131,7 +132,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: Router
 	}
 
 	async function editMemo(): Promise<void> {
-		const userDetailed = await os.api('users/show', {
+		const userDetailed = await misskeyApi('users/show', {
 			userId: user.id,
 		});
 		const { canceled, result } = await os.form(i18n.ts.editMemo, {
diff --git a/packages/frontend/src/scripts/install-plugin.ts b/packages/frontend/src/scripts/install-plugin.ts
index 1310a0dc73..ee218df018 100644
--- a/packages/frontend/src/scripts/install-plugin.ts
+++ b/packages/frontend/src/scripts/install-plugin.ts
@@ -10,6 +10,7 @@ import { Interpreter, Parser, utils } from '@syuilo/aiscript';
 import type { Plugin } from '@/store.js';
 import { ColdDeviceStorage } from '@/store.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 export type AiScriptPluginMeta = {
@@ -110,7 +111,7 @@ export async function installPlugin(code: string, meta?: AiScriptPluginMeta) {
 		}, {
 			done: async result => {
 				const { name, permissions } = result;
-				const { token } = await os.api('miauth/gen-token', {
+				const { token } = await misskeyApi('miauth/gen-token', {
 					session: null,
 					name: name,
 					permission: permissions,
diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/lookup-user.ts
index a35fe898e4..9ae5eccb7c 100644
--- a/packages/frontend/src/scripts/lookup-user.ts
+++ b/packages/frontend/src/scripts/lookup-user.ts
@@ -6,6 +6,7 @@
 import * as Misskey from 'misskey-js';
 import { i18n } from '@/i18n.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 export async function lookupUser() {
 	const { canceled, result } = await os.inputText({
@@ -17,8 +18,8 @@ export async function lookupUser() {
 		os.pageWindow(`/admin/user/${user.id}`);
 	};
 
-	const usernamePromise = os.api('users/show', Misskey.acct.parse(result));
-	const idPromise = os.api('users/show', { userId: result });
+	const usernamePromise = misskeyApi('users/show', Misskey.acct.parse(result));
+	const idPromise = misskeyApi('users/show', { userId: result });
 	let _notFound = false;
 	const notFound = () => {
 		if (_notFound) {
diff --git a/packages/frontend/src/scripts/lookup.ts b/packages/frontend/src/scripts/lookup.ts
index 979f40f038..ff438af24f 100644
--- a/packages/frontend/src/scripts/lookup.ts
+++ b/packages/frontend/src/scripts/lookup.ts
@@ -4,6 +4,7 @@
  */
 
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 import { mainRouter } from '@/router.js';
 import { Router } from '@/nirax.js';
@@ -28,7 +29,7 @@ export async function lookup(router?: Router) {
 	}
 
 	if (query.startsWith('https://')) {
-		const promise = os.api('ap/show', {
+		const promise = misskeyApi('ap/show', {
 			uri: query,
 		});
 
diff --git a/packages/frontend/src/scripts/api.ts b/packages/frontend/src/scripts/misskey-api.ts
similarity index 91%
rename from packages/frontend/src/scripts/api.ts
rename to packages/frontend/src/scripts/misskey-api.ts
index 8f3a163938..e71c5dd592 100644
--- a/packages/frontend/src/scripts/api.ts
+++ b/packages/frontend/src/scripts/misskey-api.ts
@@ -10,7 +10,7 @@ import { $i } from '@/account.js';
 export const pendingApiRequestsCount = ref(0);
 
 // Implements Misskey.api.ApiClient.request
-export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+export function misskeyApi<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
 	endpoint: E,
 	data: P = {} as any,
 	token?: string | null | undefined,
@@ -57,7 +57,7 @@ export function api<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoin
 }
 
 // Implements Misskey.api.ApiClient.request
-export function apiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
+export function misskeyApiGet<E extends keyof Misskey.Endpoints, P extends Misskey.Endpoints[E]['req']>(
 	endpoint: E,
 	data: P = {} as any,
 ): Promise<Misskey.api.SwitchCaseResponseType<E, P>> {
diff --git a/packages/frontend/src/scripts/select-file.ts b/packages/frontend/src/scripts/select-file.ts
index 53e2cd5b16..f6977eb1fb 100644
--- a/packages/frontend/src/scripts/select-file.ts
+++ b/packages/frontend/src/scripts/select-file.ts
@@ -6,6 +6,7 @@
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
@@ -65,7 +66,7 @@ export function chooseFileFromUrl(): Promise<Misskey.entities.DriveFile> {
 				}
 			});
 
-			os.api('drive/files/upload-from-url', {
+			misskeyApi('drive/files/upload-from-url', {
 				url: url,
 				folderId: defaultStore.state.uploadFolder,
 				marker,
diff --git a/packages/frontend/src/scripts/sound.ts b/packages/frontend/src/scripts/sound.ts
index 2f7545ef0d..0b966ff199 100644
--- a/packages/frontend/src/scripts/sound.ts
+++ b/packages/frontend/src/scripts/sound.ts
@@ -5,7 +5,7 @@
 
 import type { SoundStore } from '@/store.js';
 import { defaultStore } from '@/store.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 
 let ctx: AudioContext;
 const cache = new Map<string, AudioBuffer>();
@@ -120,7 +120,7 @@ export async function loadAudio(soundStore: SoundStore, options?: { useCache?: b
 		} catch (err) {
 			try {
 				// URLが変わっている可能性があるのでドライブ側からURLを取得するフォールバック
-				const apiRes = await os.api('drive/files/show', {
+				const apiRes = await misskeyApi('drive/files/show', {
 					fileId: soundStore.fileId,
 				});
 				response = await fetch(apiRes.url);
diff --git a/packages/frontend/src/theme-store.ts b/packages/frontend/src/theme-store.ts
index f37c01cca1..f96059b849 100644
--- a/packages/frontend/src/theme-store.ts
+++ b/packages/frontend/src/theme-store.ts
@@ -5,7 +5,7 @@
 
 import { Theme, getBuiltinThemes } from '@/scripts/theme.js';
 import { miLocalStorage } from '@/local-storage.js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i } from '@/account.js';
 
 const lsCacheKey = $i ? `themes:${$i.id}` as const : null;
@@ -19,7 +19,7 @@ export async function fetchThemes(): Promise<void> {
 	if ($i == null) return;
 
 	try {
-		const themes = await api('i/registry/get', { scope: ['client'], key: 'themes' });
+		const themes = await misskeyApi('i/registry/get', { scope: ['client'], key: 'themes' });
 		miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes));
 	} catch (err) {
 		if (err.code === 'NO_SUCH_KEY') return;
@@ -35,13 +35,13 @@ export async function addTheme(theme: Theme): Promise<void> {
 	}
 	await fetchThemes();
 	const themes = getThemes().concat(theme);
-	await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes });
+	await misskeyApi('i/registry/set', { scope: ['client'], key: 'themes', value: themes });
 	miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes));
 }
 
 export async function removeTheme(theme: Theme): Promise<void> {
 	if ($i == null) return;
 	const themes = getThemes().filter(t => t.id !== theme.id);
-	await api('i/registry/set', { scope: ['client'], key: 'themes', value: themes });
+	await misskeyApi('i/registry/set', { scope: ['client'], key: 'themes', value: themes });
 	miLocalStorage.setItem(lsCacheKey!, JSON.stringify(themes));
 }
diff --git a/packages/frontend/src/type.ts b/packages/frontend/src/type.ts
new file mode 100644
index 0000000000..9c0fc2a11e
--- /dev/null
+++ b/packages/frontend/src/type.ts
@@ -0,0 +1,3 @@
+export type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P] };
+
+export type WithNonNullable<T, K extends keyof T> = T & { [P in K]-?: NonNullable<T[P]> };
diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue
index 6ece7d86d7..78af49cdc2 100644
--- a/packages/frontend/src/ui/_common_/common.vue
+++ b/packages/frontend/src/ui/_common_/common.vue
@@ -49,7 +49,8 @@ import { defineAsyncComponent, ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import { swInject } from './sw-inject.js';
 import XNotification from './notification.vue';
-import { popups, pendingApiRequestsCount } from '@/os.js';
+import { popups } from '@/os.js';
+import { pendingApiRequestsCount } from '@/scripts/misskey-api.js';
 import { uploads } from '@/scripts/upload.js';
 import * as sound from '@/scripts/sound.js';
 import { $i } from '@/account.js';
diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue
index c92695afed..8df3b289de 100644
--- a/packages/frontend/src/ui/_common_/statusbar-federation.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import * as Misskey from 'misskey-js';
 import MarqueeText from '@/components/MkMarquee.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
@@ -52,7 +52,7 @@ const fetching = ref(true);
 const key = ref(0);
 
 const tick = () => {
-	os.api('federation/instances', {
+	misskeyApi('federation/instances', {
 		sort: '+latestRequestReceivedAt',
 		limit: 30,
 	}).then(res => {
diff --git a/packages/frontend/src/ui/_common_/statusbar-user-list.vue b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
index 6057174ba8..34d7b0e4e5 100644
--- a/packages/frontend/src/ui/_common_/statusbar-user-list.vue
+++ b/packages/frontend/src/ui/_common_/statusbar-user-list.vue
@@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref, watch } from 'vue';
 import * as Misskey from 'misskey-js';
 import MarqueeText from '@/components/MkMarquee.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { getNoteSummary } from '@/scripts/get-note-summary.js';
 import { notePage } from '@/filters/note.js';
@@ -54,7 +54,7 @@ const key = ref(0);
 
 const tick = () => {
 	if (props.userListId == null) return;
-	os.api('notes/user-list-timeline', {
+	misskeyApi('notes/user-list-timeline', {
 		listId: props.userListId,
 	}).then(res => {
 		notes.value = res;
diff --git a/packages/frontend/src/ui/_common_/sw-inject.ts b/packages/frontend/src/ui/_common_/sw-inject.ts
index 5239b76705..504484f8de 100644
--- a/packages/frontend/src/ui/_common_/sw-inject.ts
+++ b/packages/frontend/src/ui/_common_/sw-inject.ts
@@ -3,7 +3,8 @@
  * SPDX-License-Identifier: AGPL-3.0-only
  */
 
-import { api, post } from '@/os.js';
+import { post } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { $i, login } from '@/account.js';
 import { getAccountFromId } from '@/scripts/get-account-from-id.js';
 import { mainRouter } from '@/router.js';
@@ -30,10 +31,10 @@ export function swInject() {
 				// プッシュ通知から来たreply,renoteはtruncateBodyが通されているため、
 				// 完全なノートを取得しなおす
 				if (props.reply) {
-					props.reply = await api('notes/show', { noteId: props.reply.id });
+					props.reply = await misskeyApi('notes/show', { noteId: props.reply.id });
 				}
 				if (props.renote) {
-					props.renote = await api('notes/show', { noteId: props.renote.id });
+					props.renote = await misskeyApi('notes/show', { noteId: props.renote.id });
 				}
 				return post(props);
 			}
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index fe4d2a809c..ded24cdd26 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -19,6 +19,7 @@ import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -35,7 +36,7 @@ onMounted(() => {
 });
 
 async function setAntenna() {
-	const antennas = await os.api('antennas/list');
+	const antennas = await misskeyApi('antennas/list');
 	const { canceled, result: antenna } = await os.select({
 		title: i18n.ts.selectAntenna,
 		items: antennas.map(x => ({
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index de5d94b4f7..ccb440edff 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -26,6 +26,7 @@ import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import MkButton from '@/components/MkButton.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -41,7 +42,7 @@ if (props.column.channelId == null) {
 }
 
 async function setChannel() {
-	const channels = await os.api('channels/my-favorites', {
+	const channels = await misskeyApi('channels/my-favorites', {
 		limit: 100,
 	});
 	const { canceled, result: channel } = await os.select({
@@ -60,7 +61,7 @@ async function setChannel() {
 
 async function post() {
 	if (!channel.value || channel.value.id !== props.column.channelId) {
-		channel.value = await os.api('channels/show', {
+		channel.value = await misskeyApi('channels/show', {
 			channelId: props.column.channelId,
 		});
 	}
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 49fdf4d314..3a23cbdb4e 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -7,7 +7,7 @@ import { throttle } from 'throttle-debounce';
 import { markRaw } from 'vue';
 import { notificationTypes } from 'misskey-js';
 import { Storage } from '@/pizzax.js';
-import { api } from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { deepClone } from '@/scripts/clone.js';
 
 type ColumnWidget = {
@@ -70,7 +70,7 @@ export const loadDeck = async () => {
 	let deck;
 
 	try {
-		deck = await api('i/registry/get', {
+		deck = await misskeyApi('i/registry/get', {
 			scope: ['client', 'deck', 'profiles'],
 			key: deckStore.state.profile,
 		});
@@ -95,7 +95,7 @@ export const loadDeck = async () => {
 
 // TODO: deckがloadされていない状態でsaveすると意図せず上書きが発生するので対策する
 export const saveDeck = throttle(1000, () => {
-	api('i/registry/set', {
+	misskeyApi('i/registry/set', {
 		scope: ['client', 'deck', 'profiles'],
 		key: deckStore.state.profile,
 		value: {
@@ -106,13 +106,13 @@ export const saveDeck = throttle(1000, () => {
 });
 
 export async function getProfiles(): Promise<string[]> {
-	return await api('i/registry/keys', {
+	return await misskeyApi('i/registry/keys', {
 		scope: ['client', 'deck', 'profiles'],
 	});
 }
 
 export async function deleteProfile(key: string): Promise<void> {
-	return await api('i/registry/remove', {
+	return await misskeyApi('i/registry/remove', {
 		scope: ['client', 'deck', 'profiles'],
 		key: key,
 	});
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index 7166561c7a..84508ca78a 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -19,6 +19,7 @@ import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -40,7 +41,7 @@ watch(withRenotes, v => {
 });
 
 async function setList() {
-	const lists = await os.api('users/lists/list');
+	const lists = await misskeyApi('users/lists/list');
 	const { canceled, result: list } = await os.select({
 		title: i18n.ts.selectList,
 		items: lists.map(x => ({
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index d9bcf8d95e..5ab5a93796 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -19,6 +19,7 @@ import XColumn from './column.vue';
 import { updateColumn, Column } from './deck-store.js';
 import MkTimeline from '@/components/MkTimeline.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { i18n } from '@/i18n.js';
 
 const props = defineProps<{
@@ -35,7 +36,7 @@ onMounted(() => {
 });
 
 async function setRole() {
-	const roles = (await os.api('roles/list')).filter(x => x.isExplorable);
+	const roles = (await misskeyApi('roles/list')).filter(x => x.isExplorable);
 	const { canceled, result: role } = await os.select({
 		title: i18n.ts.role,
 		items: roles.map(x => ({
diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue
index 1fb77e42dc..5af6bc30a8 100644
--- a/packages/frontend/src/ui/visitor.vue
+++ b/packages/frontend/src/ui/visitor.vue
@@ -74,6 +74,7 @@ import * as Misskey from 'misskey-js';
 import XCommon from './_common_/common.vue';
 import { instanceName } from '@/config.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { instance } from '@/instance.js';
 import XSigninDialog from '@/components/MkSigninDialog.vue';
 import XSignupDialog from '@/components/MkSignupDialog.vue';
@@ -121,7 +122,7 @@ const keymap = computed(() => {
 
 const root = computed(() => mainRouter.currentRoute.value.name === 'index');
 
-os.api('meta', { detail: true }).then(res => {
+misskeyApi('meta', { detail: true }).then(res => {
 	meta.value = res;
 });
 
diff --git a/packages/frontend/src/widgets/WidgetActivity.vue b/packages/frontend/src/widgets/WidgetActivity.vue
index 7759986928..56364839be 100644
--- a/packages/frontend/src/widgets/WidgetActivity.vue
+++ b/packages/frontend/src/widgets/WidgetActivity.vue
@@ -25,7 +25,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid
 import XCalendar from './WidgetActivity.calendar.vue';
 import XChart from './WidgetActivity.chart.vue';
 import { GetFormResultType } from '@/scripts/form.js';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import MkContainer from '@/components/MkContainer.vue';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
@@ -76,7 +76,7 @@ const toggleView = () => {
 	save();
 };
 
-os.apiGet('charts/user/notes', {
+misskeyApiGet('charts/user/notes', {
 	userId: $i.id,
 	span: 'day',
 	limit: 7 * 21,
diff --git a/packages/frontend/src/widgets/WidgetAiscript.vue b/packages/frontend/src/widgets/WidgetAiscript.vue
index 5968b54626..a4c1025265 100644
--- a/packages/frontend/src/widgets/WidgetAiscript.vue
+++ b/packages/frontend/src/widgets/WidgetAiscript.vue
@@ -25,7 +25,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
 import MkContainer from '@/components/MkContainer.vue';
-import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import { $i } from '@/account.js';
 import { i18n } from '@/i18n.js';
 
@@ -69,19 +69,7 @@ const run = async () => {
 		storageKey: 'widget',
 		token: $i?.token,
 	}), {
-		in: (q) => {
-			return new Promise(ok => {
-				os.inputText({
-					title: q,
-				}).then(({ canceled, result: a }) => {
-					if (canceled) {
-						ok('');
-					} else {
-						ok(a);
-					}
-				});
-			});
-		},
+		in: aiScriptReadline,
 		out: (value) => {
 			logs.value.push({
 				id: Math.random().toString(),
diff --git a/packages/frontend/src/widgets/WidgetAiscriptApp.vue b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
index 10248a840a..e236253797 100644
--- a/packages/frontend/src/widgets/WidgetAiscriptApp.vue
+++ b/packages/frontend/src/widgets/WidgetAiscriptApp.vue
@@ -18,7 +18,7 @@ import { Interpreter, Parser } from '@syuilo/aiscript';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
-import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import { $i } from '@/account.js';
 import MkAsUi from '@/components/MkAsUi.vue';
 import MkContainer from '@/components/MkContainer.vue';
@@ -64,19 +64,7 @@ async function run() {
 			root.value = _root.value;
 		}),
 	}, {
-		in: (q) => {
-			return new Promise(ok => {
-				os.inputText({
-					title: q,
-				}).then(({ canceled, result: a }) => {
-					if (canceled) {
-						ok('');
-					} else {
-						ok(a);
-					}
-				});
-			});
-		},
+		in: aiScriptReadline,
 		out: (value) => {
 			// nop
 		},
diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
index 7c4455516d..9af139af6a 100644
--- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
+++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue
@@ -27,7 +27,7 @@ import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
 import { infoImageUrl } from '@/instance.js';
@@ -70,7 +70,7 @@ const fetch = () => {
 	now.setHours(0, 0, 0, 0);
 
 	if (now > lfAtD) {
-		os.api('users/following', {
+		misskeyApi('users/following', {
 			limit: 18,
 			birthday: now.toISOString(),
 			userId: $i.id,
diff --git a/packages/frontend/src/widgets/WidgetButton.vue b/packages/frontend/src/widgets/WidgetButton.vue
index 11082c1e3f..80fd000d09 100644
--- a/packages/frontend/src/widgets/WidgetButton.vue
+++ b/packages/frontend/src/widgets/WidgetButton.vue
@@ -16,7 +16,7 @@ import { Interpreter, Parser } from '@syuilo/aiscript';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
-import { createAiScriptEnv } from '@/scripts/aiscript/api.js';
+import { aiScriptReadline, createAiScriptEnv } from '@/scripts/aiscript/api.js';
 import { $i } from '@/account.js';
 import MkButton from '@/components/MkButton.vue';
 
@@ -56,19 +56,7 @@ const run = async () => {
 		storageKey: 'widget',
 		token: $i?.token,
 	}), {
-		in: (q) => {
-			return new Promise(ok => {
-				os.inputText({
-					title: q,
-				}).then(({ canceled, result: a }) => {
-					if (canceled) {
-						ok('');
-					} else {
-						ok(a);
-					}
-				});
-			});
-		},
+		in: aiScriptReadline,
 		out: (value) => {
 			// nop
 		},
diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue
index cc3ad8ff7d..bdc82a299e 100644
--- a/packages/frontend/src/widgets/WidgetFederation.vue
+++ b/packages/frontend/src/widgets/WidgetFederation.vue
@@ -31,7 +31,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
@@ -62,11 +62,11 @@ const charts = ref<Misskey.entities.ChartsInstanceResponse[]>([]);
 const fetching = ref(true);
 
 const fetch = async () => {
-	const fetchedInstances = await os.api('federation/instances', {
+	const fetchedInstances = await misskeyApi('federation/instances', {
 		sort: '+latestRequestReceivedAt',
 		limit: 5,
 	});
-	const fetchedCharts = await Promise.all(fetchedInstances.map(i => os.apiGet('charts/instance', { host: i.host, limit: 16, span: 'hour' })));
+	const fetchedCharts = await Promise.all(fetchedInstances.map(i => misskeyApiGet('charts/instance', { host: i.host, limit: 16, span: 'hour' })));
 	instances.value = fetchedInstances;
 	charts.value = fetchedCharts;
 	fetching.value = false;
diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
index 38323ed040..800cf71de0 100644
--- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue
+++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue
@@ -25,6 +25,7 @@ import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkTagCloud from '@/components/MkTagCloud.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js';
 
@@ -56,7 +57,7 @@ function onInstanceClick(i) {
 }
 
 useInterval(() => {
-	os.api('federation/instances', {
+	misskeyApi('federation/instances', {
 		sort: '+latestRequestReceivedAt',
 		limit: 25,
 	}).then(res => {
diff --git a/packages/frontend/src/widgets/WidgetJobQueue.vue b/packages/frontend/src/widgets/WidgetJobQueue.vue
index 10bc257e12..91983d8474 100644
--- a/packages/frontend/src/widgets/WidgetJobQueue.vue
+++ b/packages/frontend/src/widgets/WidgetJobQueue.vue
@@ -10,19 +10,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div class="values">
 			<div>
 				<div>Process</div>
-				<div :class="{ inc: current.inbox.activeSincePrevTick > prev.inbox.activeSincePrevTick, dec: current.inbox.activeSincePrevTick < prev.inbox.activeSincePrevTick }">{{ number(current.inbox.activeSincePrevTick) }}</div>
+				<div :class="{ inc: current.inbox.activeSincePrevTick > prev.inbox.activeSincePrevTick, dec: current.inbox.activeSincePrevTick < prev.inbox.activeSincePrevTick }" :title="`${current.inbox.activeSincePrevTick}`">{{ kmg(current.inbox.activeSincePrevTick, 2) }}</div>
 			</div>
 			<div>
 				<div>Active</div>
-				<div :class="{ inc: current.inbox.active > prev.inbox.active, dec: current.inbox.active < prev.inbox.active }">{{ number(current.inbox.active) }}</div>
+				<div :class="{ inc: current.inbox.active > prev.inbox.active, dec: current.inbox.active < prev.inbox.active }" :title="`${current.inbox.active}`">{{ kmg(current.inbox.active, 2) }}</div>
 			</div>
 			<div>
 				<div>Delayed</div>
-				<div :class="{ inc: current.inbox.delayed > prev.inbox.delayed, dec: current.inbox.delayed < prev.inbox.delayed }">{{ number(current.inbox.delayed) }}</div>
+				<div :class="{ inc: current.inbox.delayed > prev.inbox.delayed, dec: current.inbox.delayed < prev.inbox.delayed }" :title="`${current.inbox.delayed}`">{{ kmg(current.inbox.delayed, 2) }}</div>
 			</div>
 			<div>
 				<div>Waiting</div>
-				<div :class="{ inc: current.inbox.waiting > prev.inbox.waiting, dec: current.inbox.waiting < prev.inbox.waiting }">{{ number(current.inbox.waiting) }}</div>
+				<div :class="{ inc: current.inbox.waiting > prev.inbox.waiting, dec: current.inbox.waiting < prev.inbox.waiting }" :title="`${current.inbox.waiting}`">{{ kmg(current.inbox.waiting, 2) }}</div>
 			</div>
 		</div>
 	</div>
@@ -31,19 +31,19 @@ SPDX-License-Identifier: AGPL-3.0-only
 		<div class="values">
 			<div>
 				<div>Process</div>
-				<div :class="{ inc: current.deliver.activeSincePrevTick > prev.deliver.activeSincePrevTick, dec: current.deliver.activeSincePrevTick < prev.deliver.activeSincePrevTick }">{{ number(current.deliver.activeSincePrevTick) }}</div>
+				<div :class="{ inc: current.deliver.activeSincePrevTick > prev.deliver.activeSincePrevTick, dec: current.deliver.activeSincePrevTick < prev.deliver.activeSincePrevTick }" :title="`${current.deliver.activeSincePrevTick}`">{{ kmg(current.deliver.activeSincePrevTick, 2) }}</div>
 			</div>
 			<div>
 				<div>Active</div>
-				<div :class="{ inc: current.deliver.active > prev.deliver.active, dec: current.deliver.active < prev.deliver.active }">{{ number(current.deliver.active) }}</div>
+				<div :class="{ inc: current.deliver.active > prev.deliver.active, dec: current.deliver.active < prev.deliver.active }" :title="`${current.deliver.active}`">{{ kmg(current.deliver.active, 2) }}</div>
 			</div>
 			<div>
 				<div>Delayed</div>
-				<div :class="{ inc: current.deliver.delayed > prev.deliver.delayed, dec: current.deliver.delayed < prev.deliver.delayed }">{{ number(current.deliver.delayed) }}</div>
+				<div :class="{ inc: current.deliver.delayed > prev.deliver.delayed, dec: current.deliver.delayed < prev.deliver.delayed }" :title="`${current.deliver.delayed}`">{{ kmg(current.deliver.delayed, 2) }}</div>
 			</div>
 			<div>
 				<div>Waiting</div>
-				<div :class="{ inc: current.deliver.waiting > prev.deliver.waiting, dec: current.deliver.waiting < prev.deliver.waiting }">{{ number(current.deliver.waiting) }}</div>
+				<div :class="{ inc: current.deliver.waiting > prev.deliver.waiting, dec: current.deliver.waiting < prev.deliver.waiting }" :title="`${current.deliver.waiting}`">{{ kmg(current.deliver.waiting, 2) }}</div>
 			</div>
 		</div>
 	</div>
@@ -55,7 +55,7 @@ import { onUnmounted, reactive, ref } from 'vue';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import { useStream } from '@/stream.js';
-import number from '@/filters/number.js';
+import kmg from '@/filters/kmg.js';
 import * as sound from '@/scripts/sound.js';
 import { deepClone } from '@/scripts/clone.js';
 import { defaultStore } from '@/store.js';
diff --git a/packages/frontend/src/widgets/WidgetOnlineUsers.vue b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
index 0a6fec7f2e..e544a39d55 100644
--- a/packages/frontend/src/widgets/WidgetOnlineUsers.vue
+++ b/packages/frontend/src/widgets/WidgetOnlineUsers.vue
@@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only
 import { ref } from 'vue';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
-import * as os from '@/os.js';
+import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
 import number from '@/filters/number.js';
@@ -45,7 +45,7 @@ const { widgetProps, configure } = useWidgetPropsManager(name,
 const onlineUsersCount = ref(0);
 
 const tick = () => {
-	os.apiGet('get-online-users-count').then(res => {
+	misskeyApiGet('get-online-users-count').then(res => {
 		onlineUsersCount.value = res.count;
 	});
 };
diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue
index 257753ef10..253c2b9817 100644
--- a/packages/frontend/src/widgets/WidgetPhotos.vue
+++ b/packages/frontend/src/widgets/WidgetPhotos.vue
@@ -28,7 +28,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid
 import { GetFormResultType } from '@/scripts/form.js';
 import { useStream } from '@/stream.js';
 import { getStaticImageUrl } from '@/scripts/media-proxy.js';
-import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkContainer from '@/components/MkContainer.vue';
 import { defaultStore } from '@/store.js';
 import { i18n } from '@/i18n.js';
@@ -74,7 +74,7 @@ const thumbnail = (image: any): string => {
 		: image.thumbnailUrl;
 };
 
-os.api('drive/stream', {
+misskeyApi('drive/stream', {
 	type: 'image/*',
 	limit: 9,
 }).then(res => {
diff --git a/packages/frontend/src/widgets/WidgetSlideshow.vue b/packages/frontend/src/widgets/WidgetSlideshow.vue
index 7e39a05881..94bf6d7eec 100644
--- a/packages/frontend/src/widgets/WidgetSlideshow.vue
+++ b/packages/frontend/src/widgets/WidgetSlideshow.vue
@@ -22,6 +22,7 @@ import * as Misskey from 'misskey-js';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
 
@@ -77,7 +78,7 @@ const change = () => {
 const fetch = () => {
 	fetching.value = true;
 
-	os.api('drive/files', {
+	misskeyApi('drive/files', {
 		folderId: widgetProps.folderId,
 		type: 'image/*',
 		limit: 100,
diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue
index 4a7b06f1d9..8f46bc0206 100644
--- a/packages/frontend/src/widgets/WidgetTimeline.vue
+++ b/packages/frontend/src/widgets/WidgetTimeline.vue
@@ -38,6 +38,7 @@ import { ref } from 'vue';
 import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js';
 import { GetFormResultType } from '@/scripts/form.js';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkTimeline from '@/components/MkTimeline.vue';
 import { i18n } from '@/i18n.js';
@@ -95,8 +96,8 @@ const setSrc = (src) => {
 const choose = async (ev) => {
 	menuOpened.value = true;
 	const [antennas, lists] = await Promise.all([
-		os.api('antennas/list'),
-		os.api('users/lists/list'),
+		misskeyApi('antennas/list'),
+		misskeyApi('users/lists/list'),
 	]);
 	const antennaItems = antennas.map(antenna => ({
 		text: antenna.name,
diff --git a/packages/frontend/src/widgets/WidgetTrends.vue b/packages/frontend/src/widgets/WidgetTrends.vue
index ede7cb6f3b..35925a9088 100644
--- a/packages/frontend/src/widgets/WidgetTrends.vue
+++ b/packages/frontend/src/widgets/WidgetTrends.vue
@@ -30,7 +30,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import MkMiniChart from '@/components/MkMiniChart.vue';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
 import { defaultStore } from '@/store.js';
@@ -59,7 +59,7 @@ const stats = ref<Misskey.entities.HashtagsTrendResponse>([]);
 const fetching = ref(true);
 
 const fetch = () => {
-	os.apiGet('hashtags/trend').then(res => {
+	misskeyApiGet('hashtags/trend').then(res => {
 		stats.value = res;
 		fetching.value = false;
 	});
diff --git a/packages/frontend/src/widgets/WidgetUserList.vue b/packages/frontend/src/widgets/WidgetUserList.vue
index e17b2cba93..af13e58989 100644
--- a/packages/frontend/src/widgets/WidgetUserList.vue
+++ b/packages/frontend/src/widgets/WidgetUserList.vue
@@ -30,6 +30,7 @@ import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, Wid
 import { GetFormResultType } from '@/scripts/form.js';
 import MkContainer from '@/components/MkContainer.vue';
 import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
 import { useInterval } from '@/scripts/use-interval.js';
 import { i18n } from '@/i18n.js';
 import MkButton from '@/components/MkButton.vue';
@@ -64,7 +65,7 @@ const users = ref<Misskey.entities.UserDetailed[]>([]);
 const fetching = ref(true);
 
 async function chooseList() {
-	const lists = await os.api('users/lists/list');
+	const lists = await misskeyApi('users/lists/list');
 	const { canceled, result: list } = await os.select({
 		title: i18n.ts.selectList,
 		items: lists.map(x => ({
@@ -85,11 +86,11 @@ const fetch = () => {
 		return;
 	}
 
-	os.api('users/lists/show', {
+	misskeyApi('users/lists/show', {
 		listId: widgetProps.listId,
 	}).then(_list => {
 		list.value = _list;
-		os.api('users/show', {
+		misskeyApi('users/show', {
 			userIds: list.value.userIds,
 		}).then(_users => {
 			users.value = _users;
diff --git a/packages/frontend/src/widgets/server-metric/index.vue b/packages/frontend/src/widgets/server-metric/index.vue
index b4a4182653..f5e80b0d21 100644
--- a/packages/frontend/src/widgets/server-metric/index.vue
+++ b/packages/frontend/src/widgets/server-metric/index.vue
@@ -30,7 +30,7 @@ import XMemory from './mem.vue';
 import XDisk from './disk.vue';
 import MkContainer from '@/components/MkContainer.vue';
 import { GetFormResultType } from '@/scripts/form.js';
-import * as os from '@/os.js';
+import { misskeyApiGet } from '@/scripts/misskey-api.js';
 import { useStream } from '@/stream.js';
 import { i18n } from '@/i18n.js';
 
@@ -68,7 +68,7 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name,
 
 const meta = ref<Misskey.entities.ServerInfoResponse | null>(null);
 
-os.apiGet('server-info', {}).then(res => {
+misskeyApiGet('server-info', {}).then(res => {
 	meta.value = res;
 });
 
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 0c49d500da..de9f5400fd 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -636,7 +636,7 @@ export type Channels = {
             fileUpdated: (payload: DriveFile) => void;
             folderCreated: (payload: DriveFolder) => void;
             folderDeleted: (payload: DriveFolder['id']) => void;
-            folderUpdated: (payload: DriveFile) => void;
+            folderUpdated: (payload: DriveFolder) => void;
         };
         receives: null;
     };
@@ -1034,6 +1034,18 @@ export type Endpoints = Overwrite<Endpoints_2, {
             };
         };
     };
+    'signup': {
+        req: SignupRequest;
+        res: SignupResponse;
+    };
+    'signup-pending': {
+        req: SignupPendingRequest;
+        res: SignupPendingResponse;
+    };
+    'signin': {
+        req: SigninRequest;
+        res: SigninResponse;
+    };
 }>;
 
 // @public (undocumented)
@@ -1053,6 +1065,12 @@ declare namespace entities {
         EmojiUpdated,
         EmojiDeleted,
         AnnouncementCreated,
+        SignupRequest,
+        SignupResponse,
+        SignupPendingRequest,
+        SignupPendingResponse,
+        SigninRequest,
+        SigninResponse,
         EmptyRequest,
         EmptyResponse,
         AdminMetaResponse,
@@ -2619,6 +2637,47 @@ type ServerStatsLog = string[];
 // @public (undocumented)
 type Signin = components['schemas']['Signin'];
 
+// @public (undocumented)
+type SigninRequest = {
+    username: string;
+    password: string;
+    token?: string;
+};
+
+// @public (undocumented)
+type SigninResponse = {
+    id: User['id'];
+    i: string;
+};
+
+// @public (undocumented)
+type SignupPendingRequest = {
+    code: string;
+};
+
+// @public (undocumented)
+type SignupPendingResponse = {
+    id: User['id'];
+    i: string;
+};
+
+// @public (undocumented)
+type SignupRequest = {
+    username: string;
+    password: string;
+    host?: string;
+    invitationCode?: string;
+    emailAddress?: string;
+    'hcaptcha-response'?: string | null;
+    'g-recaptcha-response'?: string | null;
+    'turnstile-response'?: string | null;
+};
+
+// @public (undocumented)
+type SignupResponse = MeDetailed & {
+    token: string;
+};
+
 // @public (undocumented)
 type StatsResponse = operations['stats']['responses']['200']['content']['application/json'];
 
diff --git a/packages/misskey-js/src/api.types.ts b/packages/misskey-js/src/api.types.ts
index d97646b7cc..75ab7d91b1 100644
--- a/packages/misskey-js/src/api.types.ts
+++ b/packages/misskey-js/src/api.types.ts
@@ -1,6 +1,14 @@
 import { Endpoints as Gen } from './autogen/endpoint';
 import { UserDetailed } from './autogen/models';
 import { UsersShowRequest } from './autogen/entities';
+import {
+	SigninRequest,
+	SigninResponse,
+	SignupPendingRequest,
+	SignupPendingResponse,
+	SignupRequest,
+	SignupResponse,
+} from './entities';
 
 type Overwrite<T, U extends { [Key in keyof T]?: unknown }> = Omit<
 	T,
@@ -55,6 +63,21 @@ export type Endpoints = Overwrite<
 					$default: UserDetailed;
 				};
 			};
-		}
+		},
+		// api.jsonには載せないものなのでここで定義
+		'signup': {
+			req: SignupRequest;
+			res: SignupResponse;
+		},
+		// api.jsonには載せないものなのでここで定義
+		'signup-pending': {
+			req: SignupPendingRequest;
+			res: SignupPendingResponse;
+		},
+		// api.jsonには載せないものなのでここで定義
+		'signin': {
+			req: SigninRequest;
+			res: SigninResponse;
+		},
 	}
 >
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 2e0a9e8b50..d6a2e712df 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.2
- * generatedAt: 2024-01-05T17:21:28.094Z
+ * generatedAt: 2024-01-02T08:53:57.449Z
  */
 
 import type { SwitchCaseResponseType } from '../api.js';
@@ -10,7 +10,7 @@ declare module '../api.js' {
   export interface APIClient {
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:meta*
      */
     request<E extends 'admin/meta', P extends Endpoints[E]['req']>(
@@ -21,7 +21,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:abuse-user-reports*
      */
     request<E extends 'admin/abuse-user-reports', P extends Endpoints[E]['req']>(
@@ -32,7 +32,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'admin/accounts/create', P extends Endpoints[E]['req']>(
@@ -43,7 +43,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:account*
      */
     request<E extends 'admin/accounts/delete', P extends Endpoints[E]['req']>(
@@ -54,7 +54,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:account*
      */
     request<E extends 'admin/accounts/find-by-email', P extends Endpoints[E]['req']>(
@@ -65,7 +65,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     request<E extends 'admin/ad/create', P extends Endpoints[E]['req']>(
@@ -76,7 +76,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     request<E extends 'admin/ad/delete', P extends Endpoints[E]['req']>(
@@ -87,7 +87,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:ad*
      */
     request<E extends 'admin/ad/list', P extends Endpoints[E]['req']>(
@@ -98,7 +98,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:ad*
      */
     request<E extends 'admin/ad/update', P extends Endpoints[E]['req']>(
@@ -109,7 +109,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     request<E extends 'admin/announcements/create', P extends Endpoints[E]['req']>(
@@ -120,7 +120,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     request<E extends 'admin/announcements/delete', P extends Endpoints[E]['req']>(
@@ -131,7 +131,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:announcements*
      */
     request<E extends 'admin/announcements/list', P extends Endpoints[E]['req']>(
@@ -142,7 +142,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:announcements*
      */
     request<E extends 'admin/announcements/update', P extends Endpoints[E]['req']>(
@@ -153,7 +153,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/create', P extends Endpoints[E]['req']>(
@@ -164,7 +164,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/delete', P extends Endpoints[E]['req']>(
@@ -175,7 +175,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/list', P extends Endpoints[E]['req']>(
@@ -186,7 +186,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:avatar-decorations*
      */
     request<E extends 'admin/avatar-decorations/update', P extends Endpoints[E]['req']>(
@@ -197,7 +197,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:delete-all-files-of-a-user*
      */
     request<E extends 'admin/delete-all-files-of-a-user', P extends Endpoints[E]['req']>(
@@ -208,7 +208,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-avatar*
      */
     request<E extends 'admin/unset-user-avatar', P extends Endpoints[E]['req']>(
@@ -219,7 +219,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:unset-user-banner*
      */
     request<E extends 'admin/unset-user-banner', P extends Endpoints[E]['req']>(
@@ -230,7 +230,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
     request<E extends 'admin/drive/clean-remote-files', P extends Endpoints[E]['req']>(
@@ -241,7 +241,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:drive*
      */
     request<E extends 'admin/drive/cleanup', P extends Endpoints[E]['req']>(
@@ -252,7 +252,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
     request<E extends 'admin/drive/files', P extends Endpoints[E]['req']>(
@@ -263,7 +263,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:drive*
      */
     request<E extends 'admin/drive/show-file', P extends Endpoints[E]['req']>(
@@ -274,7 +274,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/add-aliases-bulk', P extends Endpoints[E]['req']>(
@@ -285,7 +285,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/add', P extends Endpoints[E]['req']>(
@@ -296,7 +296,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/copy', P extends Endpoints[E]['req']>(
@@ -307,7 +307,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/delete-bulk', P extends Endpoints[E]['req']>(
@@ -318,7 +318,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/delete', P extends Endpoints[E]['req']>(
@@ -329,7 +329,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -341,7 +341,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     request<E extends 'admin/emoji/list-remote', P extends Endpoints[E]['req']>(
@@ -352,7 +352,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     request<E extends 'admin/emoji/list', P extends Endpoints[E]['req']>(
@@ -363,7 +363,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/remove-aliases-bulk', P extends Endpoints[E]['req']>(
@@ -374,7 +374,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/set-aliases-bulk', P extends Endpoints[E]['req']>(
@@ -385,7 +385,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/set-category-bulk', P extends Endpoints[E]['req']>(
@@ -396,7 +396,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/set-license-bulk', P extends Endpoints[E]['req']>(
@@ -407,7 +407,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:emoji*
      */
     request<E extends 'admin/emoji/update', P extends Endpoints[E]['req']>(
@@ -418,7 +418,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/delete-all-files', P extends Endpoints[E]['req']>(
@@ -429,7 +429,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/refresh-remote-instance-metadata', P extends Endpoints[E]['req']>(
@@ -440,7 +440,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/remove-all-following', P extends Endpoints[E]['req']>(
@@ -451,7 +451,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:federation*
      */
     request<E extends 'admin/federation/update-instance', P extends Endpoints[E]['req']>(
@@ -462,7 +462,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:index-stats*
      */
     request<E extends 'admin/get-index-stats', P extends Endpoints[E]['req']>(
@@ -473,7 +473,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:table-stats*
      */
     request<E extends 'admin/get-table-stats', P extends Endpoints[E]['req']>(
@@ -484,7 +484,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:user-ips*
      */
     request<E extends 'admin/get-user-ips', P extends Endpoints[E]['req']>(
@@ -495,7 +495,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:invite-codes*
      */
     request<E extends 'admin/invite/create', P extends Endpoints[E]['req']>(
@@ -506,7 +506,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:invite-codes*
      */
     request<E extends 'admin/invite/list', P extends Endpoints[E]['req']>(
@@ -517,7 +517,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:promo*
      */
     request<E extends 'admin/promo/create', P extends Endpoints[E]['req']>(
@@ -528,7 +528,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
     request<E extends 'admin/queue/clear', P extends Endpoints[E]['req']>(
@@ -539,7 +539,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
     request<E extends 'admin/queue/deliver-delayed', P extends Endpoints[E]['req']>(
@@ -550,7 +550,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:queue*
      */
     request<E extends 'admin/queue/inbox-delayed', P extends Endpoints[E]['req']>(
@@ -561,7 +561,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:queue*
      */
     request<E extends 'admin/queue/promote', P extends Endpoints[E]['req']>(
@@ -572,7 +572,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:emoji*
      */
     request<E extends 'admin/queue/stats', P extends Endpoints[E]['req']>(
@@ -583,7 +583,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
     request<E extends 'admin/relays/add', P extends Endpoints[E]['req']>(
@@ -594,7 +594,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:relays*
      */
     request<E extends 'admin/relays/list', P extends Endpoints[E]['req']>(
@@ -605,7 +605,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:relays*
      */
     request<E extends 'admin/relays/remove', P extends Endpoints[E]['req']>(
@@ -616,7 +616,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:reset-password*
      */
     request<E extends 'admin/reset-password', P extends Endpoints[E]['req']>(
@@ -627,7 +627,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:resolve-abuse-user-report*
      */
     request<E extends 'admin/resolve-abuse-user-report', P extends Endpoints[E]['req']>(
@@ -638,7 +638,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:send-email*
      */
     request<E extends 'admin/send-email', P extends Endpoints[E]['req']>(
@@ -649,7 +649,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:server-info*
      */
     request<E extends 'admin/server-info', P extends Endpoints[E]['req']>(
@@ -660,7 +660,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:show-moderation-log*
      */
     request<E extends 'admin/show-moderation-logs', P extends Endpoints[E]['req']>(
@@ -671,7 +671,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
      */
     request<E extends 'admin/show-user', P extends Endpoints[E]['req']>(
@@ -682,7 +682,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
      */
     request<E extends 'admin/show-users', P extends Endpoints[E]['req']>(
@@ -693,7 +693,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:suspend-user*
      */
     request<E extends 'admin/suspend-user', P extends Endpoints[E]['req']>(
@@ -704,7 +704,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:unsuspend-user*
      */
     request<E extends 'admin/unsuspend-user', P extends Endpoints[E]['req']>(
@@ -715,7 +715,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:meta*
      */
     request<E extends 'admin/update-meta', P extends Endpoints[E]['req']>(
@@ -726,7 +726,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:delete-account*
      */
     request<E extends 'admin/delete-account', P extends Endpoints[E]['req']>(
@@ -737,7 +737,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:user-note*
      */
     request<E extends 'admin/update-user-note', P extends Endpoints[E]['req']>(
@@ -748,7 +748,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/create', P extends Endpoints[E]['req']>(
@@ -759,7 +759,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/delete', P extends Endpoints[E]['req']>(
@@ -770,7 +770,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
     request<E extends 'admin/roles/list', P extends Endpoints[E]['req']>(
@@ -781,7 +781,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:admin:roles*
      */
     request<E extends 'admin/roles/show', P extends Endpoints[E]['req']>(
@@ -792,7 +792,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/update', P extends Endpoints[E]['req']>(
@@ -803,7 +803,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/assign', P extends Endpoints[E]['req']>(
@@ -814,7 +814,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/unassign', P extends Endpoints[E]['req']>(
@@ -825,7 +825,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:admin:roles*
      */
     request<E extends 'admin/roles/update-default-policies', P extends Endpoints[E]['req']>(
@@ -836,7 +836,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No* / **Permission**: *read:admin:roles*
      */
     request<E extends 'admin/roles/users', P extends Endpoints[E]['req']>(
@@ -847,7 +847,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'announcements', P extends Endpoints[E]['req']>(
@@ -858,7 +858,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'antennas/create', P extends Endpoints[E]['req']>(
@@ -869,7 +869,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'antennas/delete', P extends Endpoints[E]['req']>(
@@ -880,7 +880,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'antennas/list', P extends Endpoints[E]['req']>(
@@ -891,7 +891,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'antennas/notes', P extends Endpoints[E]['req']>(
@@ -902,7 +902,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'antennas/show', P extends Endpoints[E]['req']>(
@@ -913,7 +913,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'antennas/update', P extends Endpoints[E]['req']>(
@@ -924,7 +924,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:federation*
      */
     request<E extends 'ap/get', P extends Endpoints[E]['req']>(
@@ -935,7 +935,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'ap/show', P extends Endpoints[E]['req']>(
@@ -946,7 +946,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'app/create', P extends Endpoints[E]['req']>(
@@ -957,7 +957,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'app/show', P extends Endpoints[E]['req']>(
@@ -968,7 +968,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -980,7 +980,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'auth/session/generate', P extends Endpoints[E]['req']>(
@@ -991,7 +991,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'auth/session/show', P extends Endpoints[E]['req']>(
@@ -1002,7 +1002,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'auth/session/userkey', P extends Endpoints[E]['req']>(
@@ -1013,7 +1013,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:blocks*
      */
     request<E extends 'blocking/create', P extends Endpoints[E]['req']>(
@@ -1024,7 +1024,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:blocks*
      */
     request<E extends 'blocking/delete', P extends Endpoints[E]['req']>(
@@ -1035,7 +1035,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:blocks*
      */
     request<E extends 'blocking/list', P extends Endpoints[E]['req']>(
@@ -1046,7 +1046,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
     request<E extends 'channels/create', P extends Endpoints[E]['req']>(
@@ -1057,7 +1057,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'channels/featured', P extends Endpoints[E]['req']>(
@@ -1068,7 +1068,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
     request<E extends 'channels/follow', P extends Endpoints[E]['req']>(
@@ -1079,7 +1079,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:channels*
      */
     request<E extends 'channels/followed', P extends Endpoints[E]['req']>(
@@ -1090,7 +1090,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:channels*
      */
     request<E extends 'channels/owned', P extends Endpoints[E]['req']>(
@@ -1101,7 +1101,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'channels/show', P extends Endpoints[E]['req']>(
@@ -1112,7 +1112,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'channels/timeline', P extends Endpoints[E]['req']>(
@@ -1123,7 +1123,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
     request<E extends 'channels/unfollow', P extends Endpoints[E]['req']>(
@@ -1134,7 +1134,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
     request<E extends 'channels/update', P extends Endpoints[E]['req']>(
@@ -1145,7 +1145,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
     request<E extends 'channels/favorite', P extends Endpoints[E]['req']>(
@@ -1156,7 +1156,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:channels*
      */
     request<E extends 'channels/unfavorite', P extends Endpoints[E]['req']>(
@@ -1167,7 +1167,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:channels*
      */
     request<E extends 'channels/my-favorites', P extends Endpoints[E]['req']>(
@@ -1178,7 +1178,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'channels/search', P extends Endpoints[E]['req']>(
@@ -1189,7 +1189,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/active-users', P extends Endpoints[E]['req']>(
@@ -1200,7 +1200,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/ap-request', P extends Endpoints[E]['req']>(
@@ -1211,7 +1211,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/drive', P extends Endpoints[E]['req']>(
@@ -1222,7 +1222,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/federation', P extends Endpoints[E]['req']>(
@@ -1233,7 +1233,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/instance', P extends Endpoints[E]['req']>(
@@ -1244,7 +1244,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/notes', P extends Endpoints[E]['req']>(
@@ -1255,7 +1255,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/user/drive', P extends Endpoints[E]['req']>(
@@ -1266,7 +1266,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/user/following', P extends Endpoints[E]['req']>(
@@ -1277,7 +1277,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/user/notes', P extends Endpoints[E]['req']>(
@@ -1288,7 +1288,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/user/pv', P extends Endpoints[E]['req']>(
@@ -1299,7 +1299,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/user/reactions', P extends Endpoints[E]['req']>(
@@ -1310,7 +1310,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'charts/users', P extends Endpoints[E]['req']>(
@@ -1321,7 +1321,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'clips/add-note', P extends Endpoints[E]['req']>(
@@ -1332,7 +1332,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'clips/remove-note', P extends Endpoints[E]['req']>(
@@ -1343,7 +1343,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'clips/create', P extends Endpoints[E]['req']>(
@@ -1354,7 +1354,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'clips/delete', P extends Endpoints[E]['req']>(
@@ -1365,7 +1365,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'clips/list', P extends Endpoints[E]['req']>(
@@ -1376,7 +1376,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
     request<E extends 'clips/notes', P extends Endpoints[E]['req']>(
@@ -1387,7 +1387,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
     request<E extends 'clips/show', P extends Endpoints[E]['req']>(
@@ -1398,7 +1398,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'clips/update', P extends Endpoints[E]['req']>(
@@ -1409,7 +1409,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
      */
     request<E extends 'clips/favorite', P extends Endpoints[E]['req']>(
@@ -1420,7 +1420,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:clip-favorite*
      */
     request<E extends 'clips/unfavorite', P extends Endpoints[E]['req']>(
@@ -1431,7 +1431,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:clip-favorite*
      */
     request<E extends 'clips/my-favorites', P extends Endpoints[E]['req']>(
@@ -1442,7 +1442,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive', P extends Endpoints[E]['req']>(
@@ -1453,7 +1453,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/files', P extends Endpoints[E]['req']>(
@@ -1464,7 +1464,7 @@ declare module '../api.js' {
 
     /**
      * Find the notes to which the given file is attached.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/files/attached-notes', P extends Endpoints[E]['req']>(
@@ -1475,7 +1475,7 @@ declare module '../api.js' {
 
     /**
      * Check if a given file exists.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/files/check-existence', P extends Endpoints[E]['req']>(
@@ -1486,7 +1486,7 @@ declare module '../api.js' {
 
     /**
      * Upload a new drive file.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/files/create', P extends Endpoints[E]['req']>(
@@ -1497,7 +1497,7 @@ declare module '../api.js' {
 
     /**
      * Delete an existing drive file.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/files/delete', P extends Endpoints[E]['req']>(
@@ -1508,7 +1508,7 @@ declare module '../api.js' {
 
     /**
      * Search for a drive file by a hash of the contents.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/files/find-by-hash', P extends Endpoints[E]['req']>(
@@ -1519,7 +1519,7 @@ declare module '../api.js' {
 
     /**
      * Search for a drive file by the given parameters.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/files/find', P extends Endpoints[E]['req']>(
@@ -1530,7 +1530,7 @@ declare module '../api.js' {
 
     /**
      * Show the properties of a drive file.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/files/show', P extends Endpoints[E]['req']>(
@@ -1541,7 +1541,7 @@ declare module '../api.js' {
 
     /**
      * Update the properties of a drive file.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/files/update', P extends Endpoints[E]['req']>(
@@ -1552,7 +1552,7 @@ declare module '../api.js' {
 
     /**
      * Request the server to download a new drive file from the specified URL.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/files/upload-from-url', P extends Endpoints[E]['req']>(
@@ -1563,7 +1563,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/folders', P extends Endpoints[E]['req']>(
@@ -1574,7 +1574,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/folders/create', P extends Endpoints[E]['req']>(
@@ -1585,7 +1585,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/folders/delete', P extends Endpoints[E]['req']>(
@@ -1596,7 +1596,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/folders/find', P extends Endpoints[E]['req']>(
@@ -1607,7 +1607,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/folders/show', P extends Endpoints[E]['req']>(
@@ -1618,7 +1618,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:drive*
      */
     request<E extends 'drive/folders/update', P extends Endpoints[E]['req']>(
@@ -1629,7 +1629,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:drive*
      */
     request<E extends 'drive/stream', P extends Endpoints[E]['req']>(
@@ -1640,7 +1640,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'email-address/available', P extends Endpoints[E]['req']>(
@@ -1651,7 +1651,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'endpoint', P extends Endpoints[E]['req']>(
@@ -1662,7 +1662,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'endpoints', P extends Endpoints[E]['req']>(
@@ -1673,7 +1673,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -1685,7 +1685,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/followers', P extends Endpoints[E]['req']>(
@@ -1696,7 +1696,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/following', P extends Endpoints[E]['req']>(
@@ -1707,7 +1707,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/instances', P extends Endpoints[E]['req']>(
@@ -1718,7 +1718,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/show-instance', P extends Endpoints[E]['req']>(
@@ -1729,7 +1729,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/update-remote-user', P extends Endpoints[E]['req']>(
@@ -1740,7 +1740,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/users', P extends Endpoints[E]['req']>(
@@ -1751,7 +1751,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'federation/stats', P extends Endpoints[E]['req']>(
@@ -1762,7 +1762,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/create', P extends Endpoints[E]['req']>(
@@ -1773,7 +1773,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/delete', P extends Endpoints[E]['req']>(
@@ -1784,7 +1784,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/update', P extends Endpoints[E]['req']>(
@@ -1795,7 +1795,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/update-all', P extends Endpoints[E]['req']>(
@@ -1806,7 +1806,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/invalidate', P extends Endpoints[E]['req']>(
@@ -1817,7 +1817,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/requests/accept', P extends Endpoints[E]['req']>(
@@ -1828,7 +1828,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/requests/cancel', P extends Endpoints[E]['req']>(
@@ -1839,7 +1839,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:following*
      */
     request<E extends 'following/requests/list', P extends Endpoints[E]['req']>(
@@ -1850,7 +1850,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:following*
      */
     request<E extends 'following/requests/reject', P extends Endpoints[E]['req']>(
@@ -1861,7 +1861,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'gallery/featured', P extends Endpoints[E]['req']>(
@@ -1872,7 +1872,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'gallery/popular', P extends Endpoints[E]['req']>(
@@ -1883,7 +1883,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'gallery/posts', P extends Endpoints[E]['req']>(
@@ -1894,7 +1894,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:gallery*
      */
     request<E extends 'gallery/posts/create', P extends Endpoints[E]['req']>(
@@ -1905,7 +1905,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:gallery*
      */
     request<E extends 'gallery/posts/delete', P extends Endpoints[E]['req']>(
@@ -1916,7 +1916,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
      */
     request<E extends 'gallery/posts/like', P extends Endpoints[E]['req']>(
@@ -1927,7 +1927,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'gallery/posts/show', P extends Endpoints[E]['req']>(
@@ -1938,7 +1938,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:gallery-likes*
      */
     request<E extends 'gallery/posts/unlike', P extends Endpoints[E]['req']>(
@@ -1949,7 +1949,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:gallery*
      */
     request<E extends 'gallery/posts/update', P extends Endpoints[E]['req']>(
@@ -1960,7 +1960,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'get-online-users-count', P extends Endpoints[E]['req']>(
@@ -1971,7 +1971,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'get-avatar-decorations', P extends Endpoints[E]['req']>(
@@ -1982,7 +1982,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'hashtags/list', P extends Endpoints[E]['req']>(
@@ -1993,7 +1993,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'hashtags/search', P extends Endpoints[E]['req']>(
@@ -2004,7 +2004,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'hashtags/show', P extends Endpoints[E]['req']>(
@@ -2015,7 +2015,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'hashtags/trend', P extends Endpoints[E]['req']>(
@@ -2026,7 +2026,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'hashtags/users', P extends Endpoints[E]['req']>(
@@ -2037,7 +2037,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i', P extends Endpoints[E]['req']>(
@@ -2048,7 +2048,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2060,7 +2060,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2072,7 +2072,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2084,7 +2084,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2096,7 +2096,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2108,7 +2108,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2120,7 +2120,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2132,7 +2132,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2144,7 +2144,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2156,7 +2156,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2168,7 +2168,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/claim-achievement', P extends Endpoints[E]['req']>(
@@ -2179,7 +2179,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2191,7 +2191,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2203,7 +2203,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2215,7 +2215,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2227,7 +2227,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2239,7 +2239,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2251,7 +2251,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2263,7 +2263,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2275,7 +2275,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2287,7 +2287,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:favorites*
      */
     request<E extends 'i/favorites', P extends Endpoints[E]['req']>(
@@ -2298,7 +2298,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:gallery-likes*
      */
     request<E extends 'i/gallery/likes', P extends Endpoints[E]['req']>(
@@ -2309,7 +2309,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:gallery*
      */
     request<E extends 'i/gallery/posts', P extends Endpoints[E]['req']>(
@@ -2320,7 +2320,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2332,7 +2332,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2344,7 +2344,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2356,7 +2356,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2368,7 +2368,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2380,7 +2380,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:notifications*
      */
     request<E extends 'i/notifications', P extends Endpoints[E]['req']>(
@@ -2391,7 +2391,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:notifications*
      */
     request<E extends 'i/notifications-grouped', P extends Endpoints[E]['req']>(
@@ -2402,7 +2402,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:page-likes*
      */
     request<E extends 'i/page-likes', P extends Endpoints[E]['req']>(
@@ -2413,7 +2413,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:pages*
      */
     request<E extends 'i/pages', P extends Endpoints[E]['req']>(
@@ -2424,7 +2424,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/pin', P extends Endpoints[E]['req']>(
@@ -2435,7 +2435,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/read-all-unread-notes', P extends Endpoints[E]['req']>(
@@ -2446,7 +2446,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/read-announcement', P extends Endpoints[E]['req']>(
@@ -2457,7 +2457,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2469,7 +2469,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/get-all', P extends Endpoints[E]['req']>(
@@ -2480,7 +2480,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/get-detail', P extends Endpoints[E]['req']>(
@@ -2491,7 +2491,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/get', P extends Endpoints[E]['req']>(
@@ -2502,7 +2502,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/keys-with-type', P extends Endpoints[E]['req']>(
@@ -2513,7 +2513,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/registry/keys', P extends Endpoints[E]['req']>(
@@ -2524,7 +2524,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/registry/remove', P extends Endpoints[E]['req']>(
@@ -2535,7 +2535,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2547,7 +2547,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/registry/set', P extends Endpoints[E]['req']>(
@@ -2558,7 +2558,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2570,7 +2570,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2582,7 +2582,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/unpin', P extends Endpoints[E]['req']>(
@@ -2593,7 +2593,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2605,7 +2605,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/update', P extends Endpoints[E]['req']>(
@@ -2616,7 +2616,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2628,7 +2628,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/webhooks/create', P extends Endpoints[E]['req']>(
@@ -2639,7 +2639,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/webhooks/list', P extends Endpoints[E]['req']>(
@@ -2650,7 +2650,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'i/webhooks/show', P extends Endpoints[E]['req']>(
@@ -2661,7 +2661,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/webhooks/update', P extends Endpoints[E]['req']>(
@@ -2672,7 +2672,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'i/webhooks/delete', P extends Endpoints[E]['req']>(
@@ -2683,7 +2683,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
     request<E extends 'invite/create', P extends Endpoints[E]['req']>(
@@ -2694,7 +2694,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:invite-codes*
      */
     request<E extends 'invite/delete', P extends Endpoints[E]['req']>(
@@ -2705,7 +2705,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
     request<E extends 'invite/list', P extends Endpoints[E]['req']>(
@@ -2716,7 +2716,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:invite-codes*
      */
     request<E extends 'invite/limit', P extends Endpoints[E]['req']>(
@@ -2727,7 +2727,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'meta', P extends Endpoints[E]['req']>(
@@ -2738,7 +2738,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'emojis', P extends Endpoints[E]['req']>(
@@ -2749,7 +2749,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'emoji', P extends Endpoints[E]['req']>(
@@ -2760,7 +2760,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -2772,7 +2772,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
     request<E extends 'mute/create', P extends Endpoints[E]['req']>(
@@ -2783,7 +2783,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
     request<E extends 'mute/delete', P extends Endpoints[E]['req']>(
@@ -2794,7 +2794,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:mutes*
      */
     request<E extends 'mute/list', P extends Endpoints[E]['req']>(
@@ -2805,7 +2805,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
     request<E extends 'renote-mute/create', P extends Endpoints[E]['req']>(
@@ -2816,7 +2816,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:mutes*
      */
     request<E extends 'renote-mute/delete', P extends Endpoints[E]['req']>(
@@ -2827,7 +2827,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:mutes*
      */
     request<E extends 'renote-mute/list', P extends Endpoints[E]['req']>(
@@ -2838,7 +2838,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'my/apps', P extends Endpoints[E]['req']>(
@@ -2849,7 +2849,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes', P extends Endpoints[E]['req']>(
@@ -2860,7 +2860,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/children', P extends Endpoints[E]['req']>(
@@ -2871,7 +2871,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/clips', P extends Endpoints[E]['req']>(
@@ -2882,7 +2882,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/conversation', P extends Endpoints[E]['req']>(
@@ -2893,7 +2893,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:notes*
      */
     request<E extends 'notes/create', P extends Endpoints[E]['req']>(
@@ -2904,7 +2904,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:notes*
      */
     request<E extends 'notes/delete', P extends Endpoints[E]['req']>(
@@ -2915,7 +2915,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:favorites*
      */
     request<E extends 'notes/favorites/create', P extends Endpoints[E]['req']>(
@@ -2926,7 +2926,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:favorites*
      */
     request<E extends 'notes/favorites/delete', P extends Endpoints[E]['req']>(
@@ -2937,7 +2937,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/featured', P extends Endpoints[E]['req']>(
@@ -2948,7 +2948,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/global-timeline', P extends Endpoints[E]['req']>(
@@ -2959,7 +2959,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/hybrid-timeline', P extends Endpoints[E]['req']>(
@@ -2970,7 +2970,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/local-timeline', P extends Endpoints[E]['req']>(
@@ -2981,7 +2981,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/mentions', P extends Endpoints[E]['req']>(
@@ -2992,7 +2992,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/polls/recommendation', P extends Endpoints[E]['req']>(
@@ -3003,7 +3003,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:votes*
      */
     request<E extends 'notes/polls/vote', P extends Endpoints[E]['req']>(
@@ -3014,7 +3014,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/reactions', P extends Endpoints[E]['req']>(
@@ -3025,7 +3025,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:reactions*
      */
     request<E extends 'notes/reactions/create', P extends Endpoints[E]['req']>(
@@ -3036,7 +3036,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:reactions*
      */
     request<E extends 'notes/reactions/delete', P extends Endpoints[E]['req']>(
@@ -3047,7 +3047,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/renotes', P extends Endpoints[E]['req']>(
@@ -3058,7 +3058,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/replies', P extends Endpoints[E]['req']>(
@@ -3069,7 +3069,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/search-by-tag', P extends Endpoints[E]['req']>(
@@ -3080,7 +3080,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/search', P extends Endpoints[E]['req']>(
@@ -3091,7 +3091,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'notes/show', P extends Endpoints[E]['req']>(
@@ -3102,7 +3102,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/state', P extends Endpoints[E]['req']>(
@@ -3113,7 +3113,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'notes/thread-muting/create', P extends Endpoints[E]['req']>(
@@ -3124,7 +3124,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'notes/thread-muting/delete', P extends Endpoints[E]['req']>(
@@ -3135,7 +3135,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/timeline', P extends Endpoints[E]['req']>(
@@ -3146,7 +3146,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/translate', P extends Endpoints[E]['req']>(
@@ -3157,7 +3157,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:notes*
      */
     request<E extends 'notes/unrenote', P extends Endpoints[E]['req']>(
@@ -3168,7 +3168,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'notes/user-list-timeline', P extends Endpoints[E]['req']>(
@@ -3179,7 +3179,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
     request<E extends 'notifications/create', P extends Endpoints[E]['req']>(
@@ -3190,7 +3190,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
     request<E extends 'notifications/mark-all-as-read', P extends Endpoints[E]['req']>(
@@ -3201,7 +3201,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:notifications*
      */
     request<E extends 'notifications/test-notification', P extends Endpoints[E]['req']>(
@@ -3212,7 +3212,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -3224,7 +3224,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:pages*
      */
     request<E extends 'pages/create', P extends Endpoints[E]['req']>(
@@ -3235,7 +3235,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:pages*
      */
     request<E extends 'pages/delete', P extends Endpoints[E]['req']>(
@@ -3246,7 +3246,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'pages/featured', P extends Endpoints[E]['req']>(
@@ -3257,7 +3257,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:page-likes*
      */
     request<E extends 'pages/like', P extends Endpoints[E]['req']>(
@@ -3268,7 +3268,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'pages/show', P extends Endpoints[E]['req']>(
@@ -3279,7 +3279,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:page-likes*
      */
     request<E extends 'pages/unlike', P extends Endpoints[E]['req']>(
@@ -3290,7 +3290,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:pages*
      */
     request<E extends 'pages/update', P extends Endpoints[E]['req']>(
@@ -3301,7 +3301,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:flash*
      */
     request<E extends 'flash/create', P extends Endpoints[E]['req']>(
@@ -3312,7 +3312,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:flash*
      */
     request<E extends 'flash/delete', P extends Endpoints[E]['req']>(
@@ -3323,7 +3323,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'flash/featured', P extends Endpoints[E]['req']>(
@@ -3334,7 +3334,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
      */
     request<E extends 'flash/like', P extends Endpoints[E]['req']>(
@@ -3345,7 +3345,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'flash/show', P extends Endpoints[E]['req']>(
@@ -3356,7 +3356,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:flash-likes*
      */
     request<E extends 'flash/unlike', P extends Endpoints[E]['req']>(
@@ -3367,7 +3367,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:flash*
      */
     request<E extends 'flash/update', P extends Endpoints[E]['req']>(
@@ -3378,7 +3378,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:flash*
      */
     request<E extends 'flash/my', P extends Endpoints[E]['req']>(
@@ -3389,7 +3389,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:flash-likes*
      */
     request<E extends 'flash/my-likes', P extends Endpoints[E]['req']>(
@@ -3400,7 +3400,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'ping', P extends Endpoints[E]['req']>(
@@ -3411,7 +3411,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'pinned-users', P extends Endpoints[E]['req']>(
@@ -3422,7 +3422,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'promo/read', P extends Endpoints[E]['req']>(
@@ -3433,7 +3433,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'roles/list', P extends Endpoints[E]['req']>(
@@ -3444,7 +3444,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'roles/show', P extends Endpoints[E]['req']>(
@@ -3455,7 +3455,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'roles/users', P extends Endpoints[E]['req']>(
@@ -3466,7 +3466,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'roles/notes', P extends Endpoints[E]['req']>(
@@ -3477,7 +3477,7 @@ declare module '../api.js' {
 
     /**
      * Request a users password to be reset.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'request-reset-password', P extends Endpoints[E]['req']>(
@@ -3488,7 +3488,7 @@ declare module '../api.js' {
 
     /**
      * Only available when running with <code>NODE_ENV=testing</code>. Reset the database and flush Redis.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'reset-db', P extends Endpoints[E]['req']>(
@@ -3499,7 +3499,7 @@ declare module '../api.js' {
 
     /**
      * Complete the password reset that was previously requested.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'reset-password', P extends Endpoints[E]['req']>(
@@ -3510,7 +3510,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'server-info', P extends Endpoints[E]['req']>(
@@ -3521,7 +3521,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'stats', P extends Endpoints[E]['req']>(
@@ -3532,7 +3532,7 @@ declare module '../api.js' {
 
     /**
      * Check push notification registration exists.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -3544,7 +3544,7 @@ declare module '../api.js' {
 
     /**
      * Update push notification registration.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -3556,7 +3556,7 @@ declare module '../api.js' {
 
     /**
      * Register to receive push notifications.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -3568,7 +3568,7 @@ declare module '../api.js' {
 
     /**
      * Unregister from receiving push notifications.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'sw/unregister', P extends Endpoints[E]['req']>(
@@ -3579,7 +3579,7 @@ declare module '../api.js' {
 
     /**
      * Endpoint for testing input validation.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'test', P extends Endpoints[E]['req']>(
@@ -3590,7 +3590,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'username/available', P extends Endpoints[E]['req']>(
@@ -3601,7 +3601,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users', P extends Endpoints[E]['req']>(
@@ -3612,7 +3612,7 @@ declare module '../api.js' {
 
     /**
      * Show all clips this user owns.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/clips', P extends Endpoints[E]['req']>(
@@ -3623,7 +3623,7 @@ declare module '../api.js' {
 
     /**
      * Show everyone that follows this user.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/followers', P extends Endpoints[E]['req']>(
@@ -3634,7 +3634,7 @@ declare module '../api.js' {
 
     /**
      * Show everyone that this user is following.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/following', P extends Endpoints[E]['req']>(
@@ -3645,7 +3645,7 @@ declare module '../api.js' {
 
     /**
      * Show all gallery posts by the given user.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/gallery/posts', P extends Endpoints[E]['req']>(
@@ -3656,7 +3656,7 @@ declare module '../api.js' {
 
     /**
      * Get a list of other users that the specified user frequently replies to.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/get-frequently-replied-users', P extends Endpoints[E]['req']>(
@@ -3667,7 +3667,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/featured-notes', P extends Endpoints[E]['req']>(
@@ -3678,7 +3678,7 @@ declare module '../api.js' {
 
     /**
      * Create a new list of users.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/create', P extends Endpoints[E]['req']>(
@@ -3689,7 +3689,7 @@ declare module '../api.js' {
 
     /**
      * Delete an existing list of users.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/delete', P extends Endpoints[E]['req']>(
@@ -3700,7 +3700,7 @@ declare module '../api.js' {
 
     /**
      * Show all lists that the authenticated user has created.
-     * 
+     *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
     request<E extends 'users/lists/list', P extends Endpoints[E]['req']>(
@@ -3711,7 +3711,7 @@ declare module '../api.js' {
 
     /**
      * Remove a user from a list.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/pull', P extends Endpoints[E]['req']>(
@@ -3722,7 +3722,7 @@ declare module '../api.js' {
 
     /**
      * Add a user to an existing list.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/push', P extends Endpoints[E]['req']>(
@@ -3733,7 +3733,7 @@ declare module '../api.js' {
 
     /**
      * Show the properties of a list.
-     * 
+     *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
     request<E extends 'users/lists/show', P extends Endpoints[E]['req']>(
@@ -3744,7 +3744,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/favorite', P extends Endpoints[E]['req']>(
@@ -3755,7 +3755,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/unfavorite', P extends Endpoints[E]['req']>(
@@ -3766,7 +3766,7 @@ declare module '../api.js' {
 
     /**
      * Update the properties of a list.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/update', P extends Endpoints[E]['req']>(
@@ -3777,7 +3777,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/create-from-public', P extends Endpoints[E]['req']>(
@@ -3788,7 +3788,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/lists/update-membership', P extends Endpoints[E]['req']>(
@@ -3799,7 +3799,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No* / **Permission**: *read:account*
      */
     request<E extends 'users/lists/get-memberships', P extends Endpoints[E]['req']>(
@@ -3810,7 +3810,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/notes', P extends Endpoints[E]['req']>(
@@ -3821,7 +3821,7 @@ declare module '../api.js' {
 
     /**
      * Show all pages this user created.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/pages', P extends Endpoints[E]['req']>(
@@ -3832,7 +3832,7 @@ declare module '../api.js' {
 
     /**
      * Show all flashs this user created.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/flashs', P extends Endpoints[E]['req']>(
@@ -3843,7 +3843,7 @@ declare module '../api.js' {
 
     /**
      * Show all reactions this user made.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/reactions', P extends Endpoints[E]['req']>(
@@ -3854,7 +3854,7 @@ declare module '../api.js' {
 
     /**
      * Show users that the authenticated user might be interested to follow.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'users/recommendation', P extends Endpoints[E]['req']>(
@@ -3865,7 +3865,7 @@ declare module '../api.js' {
 
     /**
      * Show the different kinds of relations between the authenticated user and the specified user(s).
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *read:account*
      */
     request<E extends 'users/relation', P extends Endpoints[E]['req']>(
@@ -3876,7 +3876,7 @@ declare module '../api.js' {
 
     /**
      * File a report.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:report-abuse*
      */
     request<E extends 'users/report-abuse', P extends Endpoints[E]['req']>(
@@ -3887,7 +3887,7 @@ declare module '../api.js' {
 
     /**
      * Search for a user by username and/or host.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/search-by-username-and-host', P extends Endpoints[E]['req']>(
@@ -3898,7 +3898,7 @@ declare module '../api.js' {
 
     /**
      * Search for users.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/search', P extends Endpoints[E]['req']>(
@@ -3909,7 +3909,7 @@ declare module '../api.js' {
 
     /**
      * Show the properties of a user.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/show', P extends Endpoints[E]['req']>(
@@ -3920,7 +3920,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'users/achievements', P extends Endpoints[E]['req']>(
@@ -3931,7 +3931,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *Yes* / **Permission**: *write:account*
      */
     request<E extends 'users/update-memo', P extends Endpoints[E]['req']>(
@@ -3942,7 +3942,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'fetch-rss', P extends Endpoints[E]['req']>(
@@ -3953,7 +3953,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Internal Endpoint**: This endpoint is an API for the misskey mainframe and is not intended for use by third parties.
      * **Credential required**: *Yes*
      */
@@ -3965,7 +3965,7 @@ declare module '../api.js' {
 
     /**
      * No description provided.
-     * 
+     *
      * **Credential required**: *No*
      */
     request<E extends 'retention', P extends Endpoints[E]['req']>(
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index cdc104fe84..192a1a31e0 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.2
- * generatedAt: 2024-01-05T17:21:28.090Z
+ * generatedAt: 2024-01-02T08:53:57.445Z
  */
 
 import type {
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index 39aea942d4..fd4d7372cc 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.2
- * generatedAt: 2024-01-05T17:21:28.088Z
+ * generatedAt: 2024-01-02T08:53:57.443Z
  */
 
 import { operations } from './types.js';
diff --git a/packages/misskey-js/src/autogen/models.ts b/packages/misskey-js/src/autogen/models.ts
index 3880b3823e..5c07e963b0 100644
--- a/packages/misskey-js/src/autogen/models.ts
+++ b/packages/misskey-js/src/autogen/models.ts
@@ -1,6 +1,6 @@
 /*
  * version: 2023.12.2
- * generatedAt: 2024-01-05T17:21:28.087Z
+ * generatedAt: 2024-01-06T10:58:05.668Z
  */
 
 import { components } from './types.js';
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 744d2a4472..c48ff1d28f 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -3,7 +3,7 @@
 
 /*
  * version: 2023.12.2
- * generatedAt: 2024-01-05T17:21:27.961Z
+ * generatedAt: 2024-01-02T08:53:56.447Z
  */
 
 /**
@@ -3788,13 +3788,14 @@ export type components = {
        * @example xxxxxxxxxx
        */
       channelId?: string | null;
-      channel?: {
+      channel?: ({
         id: string;
         name: string;
         color: string;
         isSensitive: boolean;
         allowRenoteToExternal: boolean;
-      } | null;
+        userId: string | null;
+      }) | null;
       localOnly?: boolean;
       reactionAcceptance: string | null;
       reactions: Record<string, never>;
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 99f433cc02..6314c88e0b 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -1,5 +1,5 @@
 import { ModerationLogPayloads } from './consts.js';
-import { Announcement, EmojiDetailed, Page, User, UserDetailed } from './autogen/models';
+import { Announcement, EmojiDetailed, MeDetailed, MeDetailedOnly, Page, User, UserDetailed } from './autogen/models';
 
 export * from './autogen/entities';
 export * from './autogen/models';
@@ -183,3 +183,38 @@ export type EmojiDeleted = {
 export type AnnouncementCreated = {
 	announcement: Announcement;
 };
+
+export type SignupRequest = {
+	username: string;
+	password: string;
+	host?: string;
+	invitationCode?: string;
+	emailAddress?: string;
+	'hcaptcha-response'?: string | null;
+	'g-recaptcha-response'?: string | null;
+	'turnstile-response'?: string | null;
+}
+
+export type SignupResponse = MeDetailed & {
+	token: string;
+}
+
+export type SignupPendingRequest = {
+	code: string;
+};
+
+export type SignupPendingResponse = {
+	id: User['id'],
+	i: string,
+};
+
+export type SigninRequest = {
+	username: string;
+	password: string;
+	token?: string;
+};
+
+export type SigninResponse = {
+	id: User['id'],
+	i: string,
+};
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index b94a2cc548..562c90595e 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -739,9 +739,6 @@ importers:
       eventemitter3:
         specifier: 5.0.1
         version: 5.0.1
-      gsap:
-        specifier: 3.12.4
-        version: 3.12.4
       idb-keyval:
         specifier: 6.2.1
         version: 6.2.1
@@ -877,10 +874,10 @@ importers:
         version: 7.6.5
       '@storybook/vue3':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.12)(vue@3.4.3)
+        version: 7.6.5(@vue/compiler-core@3.4.3)(vue@3.4.3)
       '@storybook/vue3-vite':
         specifier: 7.6.5
-        version: 7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.4.3)
+        version: 7.6.5(@vue/compiler-core@3.4.3)(typescript@5.3.3)(vite@5.0.10)(vue@3.4.3)
       '@testing-library/vue':
         specifier: 8.0.1
         version: 8.0.1(@vue/compiler-sfc@3.4.3)(vue@3.4.3)
@@ -7314,7 +7311,7 @@ packages:
       file-system-cache: 2.3.0
     dev: true
 
-  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.3.12)(typescript@5.3.3)(vite@5.0.10)(vue@3.4.3):
+  /@storybook/vue3-vite@7.6.5(@vue/compiler-core@3.4.3)(typescript@5.3.3)(vite@5.0.10)(vue@3.4.3):
     resolution: {integrity: sha512-7wUCq2Lrjlekftd5ha3hG0GSGbbzuc370cKkBqSmwFuOfI38z5+VeYt7nDtAlncxcpVSH7DejTGRuKTlC7NyYg==}
     engines: {node: ^14.18 || >=16}
     peerDependencies:
@@ -7322,7 +7319,7 @@ packages:
     dependencies:
       '@storybook/builder-vite': 7.6.5(typescript@5.3.3)(vite@5.0.10)
       '@storybook/core-server': 7.6.5
-      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.3.12)(vue@3.4.3)
+      '@storybook/vue3': 7.6.5(@vue/compiler-core@3.4.3)(vue@3.4.3)
       '@vitejs/plugin-vue': 4.5.2(vite@5.0.10)(vue@3.4.3)
       magic-string: 0.30.5
       vite: 5.0.10(@types/node@20.10.5)(sass@1.69.5)(terser@5.26.0)
@@ -7339,7 +7336,7 @@ packages:
       - vue
     dev: true
 
-  /@storybook/vue3@7.6.5(@vue/compiler-core@3.3.12)(vue@3.4.3):
+  /@storybook/vue3@7.6.5(@vue/compiler-core@3.4.3)(vue@3.4.3):
     resolution: {integrity: sha512-tv/9rVc3XXDOJu5hfZtKhrhM8x4GTLKon62Rmaxlq06weqkGlfBi/V/g1EZ7OE71Pi+woKS/TX7p9qbRrvgahg==}
     engines: {node: '>=16.0.0'}
     peerDependencies:
@@ -7351,7 +7348,7 @@ packages:
       '@storybook/global': 5.0.0
       '@storybook/preview-api': 7.6.5
       '@storybook/types': 7.6.5
-      '@vue/compiler-core': 3.3.12
+      '@vue/compiler-core': 3.4.3
       lodash: 4.17.21
       ts-dedent: 2.2.0
       type-fest: 2.19.0
@@ -12981,10 +12978,6 @@ packages:
     engines: {node: ^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0}
     dev: true
 
-  /gsap@3.12.4:
-    resolution: {integrity: sha512-1ByAq8dD0W4aBZ/JArgaQvc0gyUfkGkP8mgAQa0qZGdpOKlSOhOf+WNXjoLimKaKG3Z4Iu6DKZtnyszqQeyqWQ==}
-    dev: false
-
   /gunzip-maybe@1.4.2:
     resolution: {integrity: sha512-4haO1M4mLO91PW57BMsDFf75UmwoRX0GkdD+Faw+Lr+r/OZrOCS0pIBwOL1xCKQqnQzbNFGgK2V2CpBUPeFNTw==}
     hasBin: true