@dubeyvishal/orbital-cli 1.1.9 → 1.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -6
- package/server/src/cli/commands/auth/login.js +62 -30
- package/server/prisma/migrations/20260105143219_test_migration/migration.sql +0 -7
- package/server/prisma/migrations/20260105151026_authentication/migration.sql +0 -78
- package/server/prisma/migrations/20260114105919_add_devicecode_conversation_message/migration.sql +0 -50
- package/server/prisma/migrations/migration_lock.toml +0 -3
- package/server/prisma/schema.prisma +0 -117
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dubeyvishal/orbital-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.9",
|
|
4
4
|
"description": "A fullstack CLI-based AI platform with chat mode, multi-tool agents, and agentic AI workflows. Includes GitHub login with device authorization, secure authentication, and modular client–server architecture for building intelligent automation tools.",
|
|
5
5
|
"author": "Vishal Dubey",
|
|
6
6
|
"license": "MIT",
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"server/src",
|
|
14
|
-
"server/prisma",
|
|
15
14
|
"README.md"
|
|
16
15
|
],
|
|
17
16
|
"publishConfig": {
|
|
@@ -23,9 +22,7 @@
|
|
|
23
22
|
"dependencies": {
|
|
24
23
|
"@ai-sdk/google": "^3.0.6",
|
|
25
24
|
"@clack/prompts": "^0.11.0",
|
|
26
|
-
"@prisma/client": "^5.22.0",
|
|
27
25
|
"ai": "^6.0.29",
|
|
28
|
-
"better-auth": "^1.4.10",
|
|
29
26
|
"boxen": "^8.0.1",
|
|
30
27
|
"chalk": "^5.6.2",
|
|
31
28
|
"commander": "^14.0.2",
|
|
@@ -35,12 +32,10 @@
|
|
|
35
32
|
"marked": "^15.0.12",
|
|
36
33
|
"marked-terminal": "^7.3.0",
|
|
37
34
|
"open": "^11.0.0",
|
|
38
|
-
"prisma": "^5.22.0",
|
|
39
35
|
"yocto-spinner": "^1.0.0",
|
|
40
36
|
"zod": "^4.3.5"
|
|
41
37
|
},
|
|
42
38
|
"scripts": {
|
|
43
|
-
"postinstall": "npx prisma generate --schema=./server/prisma/schema.prisma",
|
|
44
39
|
"install-all": "npm install --prefix client && npm install --prefix server",
|
|
45
40
|
"start-client": "npm start --prefix client",
|
|
46
41
|
"start-server": "npm start --prefix server",
|
|
@@ -3,13 +3,10 @@ import { cancel, confirm, intro, outro, isCancel } from "@clack/prompts";
|
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { Command } from "commander";
|
|
5
5
|
import open from "open";
|
|
6
|
-
import os from "os";
|
|
7
6
|
import path from "path";
|
|
8
7
|
import yoctoSpinner from "yocto-spinner";
|
|
9
8
|
import * as z from "zod";
|
|
10
|
-
|
|
11
|
-
import { deviceAuthorizationClient } from "better-auth/client/plugins";
|
|
12
|
-
import { logger } from "better-auth";
|
|
9
|
+
|
|
13
10
|
import { fileURLToPath } from "url";
|
|
14
11
|
import { getStoredToken, isTokenExpired, storeToken ,TOKEN_FILE } from "../../../lib/token.js";
|
|
15
12
|
import { API_BASE } from "../../../config/api.js";
|
|
@@ -17,12 +14,56 @@ import { apiRequestSafe } from "../../utils/apiClient.js";
|
|
|
17
14
|
import { requireGeminiApiKey } from "../../../lib/orbitalConfig.js";
|
|
18
15
|
|
|
19
16
|
|
|
20
|
-
|
|
21
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
22
18
|
const __dirname = path.dirname(__filename);
|
|
23
19
|
|
|
24
20
|
const URL = API_BASE;
|
|
25
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Request a device authorization code from the server via direct fetch.
|
|
24
|
+
* Replaces: authClient.device.code()
|
|
25
|
+
*/
|
|
26
|
+
const requestDeviceCode = async (serverUrl, clientId, scope) => {
|
|
27
|
+
const res = await fetch(`${serverUrl}/api/auth/device/authorize`, {
|
|
28
|
+
method: "POST",
|
|
29
|
+
headers: { "Content-Type": "application/json" },
|
|
30
|
+
body: JSON.stringify({ client_id: clientId, scope }),
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const body = await res.json();
|
|
34
|
+
|
|
35
|
+
if (!res.ok) {
|
|
36
|
+
return { data: null, error: body };
|
|
37
|
+
}
|
|
38
|
+
return { data: body, error: null };
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Poll for a device token from the server via direct fetch.
|
|
43
|
+
* Replaces: authClient.device.token()
|
|
44
|
+
*/
|
|
45
|
+
const requestDeviceToken = async (serverUrl, deviceCode, clientId) => {
|
|
46
|
+
const res = await fetch(`${serverUrl}/api/auth/device/token`, {
|
|
47
|
+
method: "POST",
|
|
48
|
+
headers: {
|
|
49
|
+
"Content-Type": "application/json",
|
|
50
|
+
"user-agent": "Orbital CLI",
|
|
51
|
+
},
|
|
52
|
+
body: JSON.stringify({
|
|
53
|
+
grant_type: "urn:ietf:params:oauth:grant-type:device_code",
|
|
54
|
+
device_code: deviceCode,
|
|
55
|
+
client_id: clientId,
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
const body = await res.json();
|
|
60
|
+
|
|
61
|
+
if (!res.ok) {
|
|
62
|
+
return { data: null, error: body };
|
|
63
|
+
}
|
|
64
|
+
return { data: body, error: null };
|
|
65
|
+
};
|
|
66
|
+
|
|
26
67
|
const resolveClientId = async (cliClientId) => {
|
|
27
68
|
const resolved = (cliClientId || "").trim();
|
|
28
69
|
if (resolved.length > 0) return resolved;
|
|
@@ -85,20 +126,16 @@ export const loginAction = async (cmdOptions) => {
|
|
|
85
126
|
}
|
|
86
127
|
}
|
|
87
128
|
|
|
88
|
-
const authClient = createAuthClient({
|
|
89
|
-
baseURL: serverUrl,
|
|
90
|
-
plugins: [deviceAuthorizationClient()],
|
|
91
|
-
});
|
|
92
|
-
|
|
93
129
|
const spinner = yoctoSpinner({
|
|
94
130
|
text: "Requesting device authorization...",
|
|
95
131
|
}).start();
|
|
96
132
|
|
|
97
133
|
try {
|
|
98
|
-
const { data, error } = await
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
134
|
+
const { data, error } = await requestDeviceCode(
|
|
135
|
+
serverUrl,
|
|
136
|
+
clientId,
|
|
137
|
+
"openid profile email",
|
|
138
|
+
);
|
|
102
139
|
|
|
103
140
|
spinner.stop();
|
|
104
141
|
|
|
@@ -150,7 +187,7 @@ export const loginAction = async (cmdOptions) => {
|
|
|
150
187
|
);
|
|
151
188
|
|
|
152
189
|
const token = await pollForToken(
|
|
153
|
-
|
|
190
|
+
serverUrl,
|
|
154
191
|
device_code,
|
|
155
192
|
clientId,
|
|
156
193
|
interval,
|
|
@@ -179,7 +216,7 @@ export const loginAction = async (cmdOptions) => {
|
|
|
179
216
|
};
|
|
180
217
|
|
|
181
218
|
const pollForToken = async (
|
|
182
|
-
|
|
219
|
+
serverUrl,
|
|
183
220
|
deviceCode,
|
|
184
221
|
clientId,
|
|
185
222
|
initialInterval,
|
|
@@ -199,16 +236,11 @@ const pollForToken = async (
|
|
|
199
236
|
if (!spinner.isSpinning) spinner.start();
|
|
200
237
|
|
|
201
238
|
try {
|
|
202
|
-
const { data, error } = await
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
headers: {
|
|
208
|
-
"user-agent": `My CLI`,
|
|
209
|
-
},
|
|
210
|
-
},
|
|
211
|
-
});
|
|
239
|
+
const { data, error } = await requestDeviceToken(
|
|
240
|
+
serverUrl,
|
|
241
|
+
deviceCode,
|
|
242
|
+
clientId,
|
|
243
|
+
);
|
|
212
244
|
|
|
213
245
|
if (data?.access_token) {
|
|
214
246
|
spinner.stop();
|
|
@@ -233,7 +265,7 @@ const pollForToken = async (
|
|
|
233
265
|
return;
|
|
234
266
|
default:
|
|
235
267
|
spinner.stop();
|
|
236
|
-
|
|
268
|
+
console.error(`Error: ${error.error_description}`);
|
|
237
269
|
reject(
|
|
238
270
|
new Error(error.error_description || "Unknown device error"),
|
|
239
271
|
);
|
|
@@ -242,7 +274,7 @@ const pollForToken = async (
|
|
|
242
274
|
}
|
|
243
275
|
} catch (err) {
|
|
244
276
|
spinner.stop();
|
|
245
|
-
|
|
277
|
+
console.error(`Error: ${err?.message || err}`);
|
|
246
278
|
return;
|
|
247
279
|
}
|
|
248
280
|
|
|
@@ -254,7 +286,7 @@ const pollForToken = async (
|
|
|
254
286
|
};
|
|
255
287
|
|
|
256
288
|
export const login = new Command("login")
|
|
257
|
-
.description("Login to
|
|
258
|
-
.option("--server-url <url>", "The
|
|
289
|
+
.description("Login to Orbital CLI")
|
|
290
|
+
.option("--server-url <url>", "The server URL", URL)
|
|
259
291
|
.option("--client-id <id>", "The OAuth client ID")
|
|
260
292
|
.action(loginAction);
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
-- CreateTable
|
|
2
|
-
CREATE TABLE "user" (
|
|
3
|
-
"id" TEXT NOT NULL,
|
|
4
|
-
"name" TEXT NOT NULL,
|
|
5
|
-
"email" TEXT NOT NULL,
|
|
6
|
-
"emailVerified" BOOLEAN NOT NULL DEFAULT false,
|
|
7
|
-
"image" TEXT,
|
|
8
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
9
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
10
|
-
|
|
11
|
-
CONSTRAINT "user_pkey" PRIMARY KEY ("id")
|
|
12
|
-
);
|
|
13
|
-
|
|
14
|
-
-- CreateTable
|
|
15
|
-
CREATE TABLE "session" (
|
|
16
|
-
"id" TEXT NOT NULL,
|
|
17
|
-
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
18
|
-
"token" TEXT NOT NULL,
|
|
19
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
20
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
21
|
-
"ipAddress" TEXT,
|
|
22
|
-
"userAgent" TEXT,
|
|
23
|
-
"userId" TEXT NOT NULL,
|
|
24
|
-
|
|
25
|
-
CONSTRAINT "session_pkey" PRIMARY KEY ("id")
|
|
26
|
-
);
|
|
27
|
-
|
|
28
|
-
-- CreateTable
|
|
29
|
-
CREATE TABLE "account" (
|
|
30
|
-
"id" TEXT NOT NULL,
|
|
31
|
-
"accountId" TEXT NOT NULL,
|
|
32
|
-
"providerId" TEXT NOT NULL,
|
|
33
|
-
"userId" TEXT NOT NULL,
|
|
34
|
-
"accessToken" TEXT,
|
|
35
|
-
"refreshToken" TEXT,
|
|
36
|
-
"idToken" TEXT,
|
|
37
|
-
"accessTokenExpiresAt" TIMESTAMP(3),
|
|
38
|
-
"refreshTokenExpiresAt" TIMESTAMP(3),
|
|
39
|
-
"scope" TEXT,
|
|
40
|
-
"password" TEXT,
|
|
41
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
42
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
43
|
-
|
|
44
|
-
CONSTRAINT "account_pkey" PRIMARY KEY ("id")
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
-- CreateTable
|
|
48
|
-
CREATE TABLE "verification" (
|
|
49
|
-
"id" TEXT NOT NULL,
|
|
50
|
-
"identifier" TEXT NOT NULL,
|
|
51
|
-
"value" TEXT NOT NULL,
|
|
52
|
-
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
53
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
54
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
55
|
-
|
|
56
|
-
CONSTRAINT "verification_pkey" PRIMARY KEY ("id")
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
-- CreateIndex
|
|
60
|
-
CREATE UNIQUE INDEX "user_email_key" ON "user"("email");
|
|
61
|
-
|
|
62
|
-
-- CreateIndex
|
|
63
|
-
CREATE INDEX "session_userId_idx" ON "session"("userId");
|
|
64
|
-
|
|
65
|
-
-- CreateIndex
|
|
66
|
-
CREATE UNIQUE INDEX "session_token_key" ON "session"("token");
|
|
67
|
-
|
|
68
|
-
-- CreateIndex
|
|
69
|
-
CREATE INDEX "account_userId_idx" ON "account"("userId");
|
|
70
|
-
|
|
71
|
-
-- CreateIndex
|
|
72
|
-
CREATE INDEX "verification_identifier_idx" ON "verification"("identifier");
|
|
73
|
-
|
|
74
|
-
-- AddForeignKey
|
|
75
|
-
ALTER TABLE "session" ADD CONSTRAINT "session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
76
|
-
|
|
77
|
-
-- AddForeignKey
|
|
78
|
-
ALTER TABLE "account" ADD CONSTRAINT "account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
package/server/prisma/migrations/20260114105919_add_devicecode_conversation_message/migration.sql
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
-- CreateTable
|
|
2
|
-
CREATE TABLE "deviceCode" (
|
|
3
|
-
"id" TEXT NOT NULL,
|
|
4
|
-
"deviceCode" TEXT NOT NULL,
|
|
5
|
-
"userCode" TEXT NOT NULL,
|
|
6
|
-
"userId" TEXT,
|
|
7
|
-
"expiresAt" TIMESTAMP(3) NOT NULL,
|
|
8
|
-
"status" TEXT NOT NULL,
|
|
9
|
-
"lastPolledAt" TIMESTAMP(3),
|
|
10
|
-
"pollingInterval" INTEGER,
|
|
11
|
-
"clientId" TEXT,
|
|
12
|
-
"scope" TEXT,
|
|
13
|
-
|
|
14
|
-
CONSTRAINT "deviceCode_pkey" PRIMARY KEY ("id")
|
|
15
|
-
);
|
|
16
|
-
|
|
17
|
-
-- CreateTable
|
|
18
|
-
CREATE TABLE "conversation" (
|
|
19
|
-
"id" TEXT NOT NULL,
|
|
20
|
-
"userId" TEXT NOT NULL,
|
|
21
|
-
"title" TEXT NOT NULL,
|
|
22
|
-
"mode" TEXT NOT NULL DEFAULT 'chat',
|
|
23
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
24
|
-
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
25
|
-
|
|
26
|
-
CONSTRAINT "conversation_pkey" PRIMARY KEY ("id")
|
|
27
|
-
);
|
|
28
|
-
|
|
29
|
-
-- CreateTable
|
|
30
|
-
CREATE TABLE "message" (
|
|
31
|
-
"id" TEXT NOT NULL,
|
|
32
|
-
"conversationId" TEXT NOT NULL,
|
|
33
|
-
"role" TEXT NOT NULL,
|
|
34
|
-
"content" TEXT NOT NULL,
|
|
35
|
-
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
36
|
-
|
|
37
|
-
CONSTRAINT "message_pkey" PRIMARY KEY ("id")
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
-- CreateIndex
|
|
41
|
-
CREATE INDEX "conversation_userId_idx" ON "conversation"("userId");
|
|
42
|
-
|
|
43
|
-
-- CreateIndex
|
|
44
|
-
CREATE INDEX "message_conversationId_idx" ON "message"("conversationId");
|
|
45
|
-
|
|
46
|
-
-- AddForeignKey
|
|
47
|
-
ALTER TABLE "conversation" ADD CONSTRAINT "conversation_userId_fkey" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
48
|
-
|
|
49
|
-
-- AddForeignKey
|
|
50
|
-
ALTER TABLE "message" ADD CONSTRAINT "message_conversationId_fkey" FOREIGN KEY ("conversationId") REFERENCES "conversation"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
generator client {
|
|
2
|
-
provider = "prisma-client-js"
|
|
3
|
-
}
|
|
4
|
-
|
|
5
|
-
datasource db {
|
|
6
|
-
provider = "postgresql"
|
|
7
|
-
url = env("DATABASE_URL")
|
|
8
|
-
directUrl = env("DIRECT_DATABASE_URL")
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
model Test {
|
|
12
|
-
id String @id @default(cuid())
|
|
13
|
-
name String
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
model User {
|
|
17
|
-
id String @id @default(cuid())
|
|
18
|
-
name String
|
|
19
|
-
email String @unique
|
|
20
|
-
emailVerified Boolean @default(false)
|
|
21
|
-
image String?
|
|
22
|
-
createdAt DateTime @default(now())
|
|
23
|
-
updatedAt DateTime @updatedAt
|
|
24
|
-
accounts Account[]
|
|
25
|
-
conversations Conversation[]
|
|
26
|
-
sessions Session[]
|
|
27
|
-
|
|
28
|
-
@@map("user")
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
model Session {
|
|
32
|
-
id String @id @default(cuid())
|
|
33
|
-
expiresAt DateTime
|
|
34
|
-
token String @unique
|
|
35
|
-
createdAt DateTime @default(now())
|
|
36
|
-
updatedAt DateTime @updatedAt
|
|
37
|
-
ipAddress String?
|
|
38
|
-
userAgent String?
|
|
39
|
-
userId String
|
|
40
|
-
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
41
|
-
|
|
42
|
-
@@index([userId])
|
|
43
|
-
@@map("session")
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
model Account {
|
|
47
|
-
id String @id @default(cuid())
|
|
48
|
-
accountId String
|
|
49
|
-
providerId String
|
|
50
|
-
userId String
|
|
51
|
-
accessToken String?
|
|
52
|
-
refreshToken String?
|
|
53
|
-
idToken String?
|
|
54
|
-
accessTokenExpiresAt DateTime?
|
|
55
|
-
refreshTokenExpiresAt DateTime?
|
|
56
|
-
scope String?
|
|
57
|
-
password String?
|
|
58
|
-
createdAt DateTime @default(now())
|
|
59
|
-
updatedAt DateTime @updatedAt
|
|
60
|
-
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
61
|
-
|
|
62
|
-
@@index([userId])
|
|
63
|
-
@@map("account")
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
model Verification {
|
|
67
|
-
id String @id @default(cuid())
|
|
68
|
-
identifier String
|
|
69
|
-
value String
|
|
70
|
-
expiresAt DateTime
|
|
71
|
-
createdAt DateTime @default(now())
|
|
72
|
-
updatedAt DateTime @updatedAt
|
|
73
|
-
|
|
74
|
-
@@index([identifier])
|
|
75
|
-
@@map("verification")
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
model DeviceCode {
|
|
79
|
-
id String @id @default(cuid())
|
|
80
|
-
deviceCode String
|
|
81
|
-
userCode String
|
|
82
|
-
userId String?
|
|
83
|
-
expiresAt DateTime
|
|
84
|
-
status String
|
|
85
|
-
lastPolledAt DateTime?
|
|
86
|
-
pollingInterval Int?
|
|
87
|
-
clientId String?
|
|
88
|
-
scope String?
|
|
89
|
-
|
|
90
|
-
@@map("deviceCode")
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
model Conversation {
|
|
94
|
-
id String @id @default(cuid())
|
|
95
|
-
userId String
|
|
96
|
-
title String
|
|
97
|
-
mode String @default("chat")
|
|
98
|
-
createdAt DateTime @default(now())
|
|
99
|
-
updatedAt DateTime @updatedAt
|
|
100
|
-
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
|
101
|
-
messages Message[]
|
|
102
|
-
|
|
103
|
-
@@index([userId])
|
|
104
|
-
@@map("conversation")
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
model Message {
|
|
108
|
-
id String @id @default(cuid())
|
|
109
|
-
conversationId String
|
|
110
|
-
role String
|
|
111
|
-
content String
|
|
112
|
-
createdAt DateTime @default(now())
|
|
113
|
-
conversation Conversation @relation(fields: [conversationId], references: [id], onDelete: Cascade)
|
|
114
|
-
|
|
115
|
-
@@index([conversationId])
|
|
116
|
-
@@map("message")
|
|
117
|
-
}
|