skydive-cli 0.1.0-beta.389 → 0.1.0-beta.393
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/README.md
CHANGED
|
@@ -25,7 +25,10 @@ single `config.json`:
|
|
|
25
25
|
same machine replaces it rather than accumulating rows, and
|
|
26
26
|
`skydive auth logout` revokes it, so signing out doesn't leave a live key
|
|
27
27
|
behind. The workspace is in the name because the key is pinned to it: unlike
|
|
28
|
-
the session, a key never follows `workspace switch
|
|
28
|
+
the session, a key never follows `workspace switch` — and whenever the key
|
|
29
|
+
(not the session) is the credential actually driving a management command,
|
|
30
|
+
the CLI says so on stderr, naming the pinned workspace, so commands never
|
|
31
|
+
silently act in a workspace you switched away from.
|
|
29
32
|
- a **user session** for `skydive chat`. Chat is user-level and multi-agent,
|
|
30
33
|
so it authenticates as you — and unlike the API key (which is pinned to the
|
|
31
34
|
workspace that minted it), the session follows `skydive workspace switch`.
|
package/dist/js/bin.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { A as getStoredApiKeyId, C as DEFAULT_WEB_URL, E as getPromptHistoryPath, F as
|
|
2
|
+
import { A as getStoredApiKeyId, C as DEFAULT_WEB_URL, E as getPromptHistoryPath, F as resolveManagementAuth, I as resolveSession, M as getUpdateCheckDisabled, N as resolveAppUrl, P as resolveConfig, R as saveConfig, T as getConfigPath, _ as setActiveWorkspace, b as API_KEY_PREFIX, d as themes, g as listWorkspaces, h as getSessionIdentity, j as getStoredApiKeyWorkspaceName, k as getShareMachineDefault, m as getActiveWorkspaceId, p as ensureActiveOrganization, v as API_KEYS_URL, w as deleteConfig, x as DEFAULT_API_URL, y as API_KEY_FAMILY_PREFIX, z as saveSession } from "./theme-C_Fqxi-U.mjs";
|
|
3
3
|
import { n as createRestClient } from "./rest-DTlkPko_.mjs";
|
|
4
4
|
import { i as resolveAgent } from "./print-BhfjRrxI.mjs";
|
|
5
5
|
import { a as registerPortalDevice, c as machineIdentity, n as findThisDevice, o as revokePortalAccess, r as grantPortalAccess, t as fetchPortalDevices } from "./api-DRpbKHz6.mjs";
|
|
@@ -19,7 +19,7 @@ import semver from "semver";
|
|
|
19
19
|
|
|
20
20
|
//#region package.json
|
|
21
21
|
var name = "skydive-cli";
|
|
22
|
-
var version = "0.1.0-beta.
|
|
22
|
+
var version = "0.1.0-beta.393";
|
|
23
23
|
|
|
24
24
|
//#endregion
|
|
25
25
|
//#region src/types.ts
|
|
@@ -578,7 +578,8 @@ async function runBrowserLogin(argv) {
|
|
|
578
578
|
if (minted.isOk()) saveConfig({
|
|
579
579
|
apiKey: minted.value.key,
|
|
580
580
|
apiUrl: appUrl,
|
|
581
|
-
apiKeyId: minted.value.id
|
|
581
|
+
apiKeyId: minted.value.id,
|
|
582
|
+
workspaceName: identity?.activeWorkspaceName ?? null
|
|
582
583
|
});
|
|
583
584
|
else process.stderr.write(`Warning: signed in, but could not mint an API key (${minted.error.message}). Management commands will use your session; run \`skydive auth login\` again to retry.
|
|
584
585
|
`);
|
|
@@ -623,7 +624,8 @@ async function runApiKeyLogin(argv, apiKey) {
|
|
|
623
624
|
saveConfig({
|
|
624
625
|
apiKey,
|
|
625
626
|
apiUrl,
|
|
626
|
-
apiKeyId: null
|
|
627
|
+
apiKeyId: null,
|
|
628
|
+
workspaceName: null
|
|
627
629
|
});
|
|
628
630
|
if (argv.json) output(argv, {
|
|
629
631
|
authenticated: true,
|
|
@@ -694,6 +696,7 @@ const statusCommand$1 = {
|
|
|
694
696
|
workspaceName: identity?.activeWorkspaceName ?? null
|
|
695
697
|
},
|
|
696
698
|
managementAuth: management.isOk() ? management.value.kind : null,
|
|
699
|
+
keyWorkspaceName: getStoredApiKeyWorkspaceName(),
|
|
697
700
|
configPath: getConfigPath()
|
|
698
701
|
});
|
|
699
702
|
return;
|
|
@@ -706,6 +709,8 @@ const statusCommand$1 = {
|
|
|
706
709
|
console.log("API key:");
|
|
707
710
|
console.log(` Key: ${apiKey.value.apiKey.slice(0, 12)}...`);
|
|
708
711
|
console.log(` API: ${apiKey.value.apiUrl}`);
|
|
712
|
+
const pinned = getStoredApiKeyWorkspaceName();
|
|
713
|
+
if (pinned) console.log(` Space: ${pinned} (the key always acts here)`);
|
|
709
714
|
} else console.log("API key: not configured.");
|
|
710
715
|
if (session.isOk()) {
|
|
711
716
|
console.log("Chat session:");
|
|
@@ -721,7 +726,7 @@ const statusCommand$1 = {
|
|
|
721
726
|
}
|
|
722
727
|
} else console.log("Chat session: not signed in.");
|
|
723
728
|
if (management.isOk()) {
|
|
724
|
-
const via = management.value.kind === "session" ? "chat session (follows `workspace switch`)" : "API key (
|
|
729
|
+
const via = management.value.kind === "session" ? "chat session (follows `workspace switch`)" : management.value.pinnedWorkspaceName ? `API key (always acts in "${management.value.pinnedWorkspaceName}")` : "API key (always acts in the workspace it was created in)";
|
|
725
730
|
console.log(`Management commands use: ${via}`);
|
|
726
731
|
}
|
|
727
732
|
console.log(`Config: ${getConfigPath()}`);
|
|
@@ -760,6 +765,8 @@ function requireManagementClient(argv) {
|
|
|
760
765
|
printError(auth.error.message);
|
|
761
766
|
process.exit(1);
|
|
762
767
|
}
|
|
768
|
+
if (auth.value.kind === "api-key" && auth.value.pinnedWorkspaceName) process.stderr.write(`Using this machine's API key, which always acts in the "${auth.value.pinnedWorkspaceName}" workspace (even after \`workspace switch\`). Run \`skydive auth login\` to change that.
|
|
769
|
+
`);
|
|
763
770
|
return new SkydiveApiClient(auth.value);
|
|
764
771
|
}
|
|
765
772
|
/** Resolve the session (see {@link requireSession}) and build a REST client. */
|
|
@@ -1416,7 +1423,7 @@ const chatCommand = {
|
|
|
1416
1423
|
printError(`Unknown theme "${themeId}". Valid themes: ${themes.map((t) => t.id).join(", ")}`);
|
|
1417
1424
|
process.exit(1);
|
|
1418
1425
|
}
|
|
1419
|
-
const { runChat } = await import("./boot-
|
|
1426
|
+
const { runChat } = await import("./boot-B-AxTj84.mjs");
|
|
1420
1427
|
await runChat({
|
|
1421
1428
|
appUrl,
|
|
1422
1429
|
sessionToken: session.value.sessionToken,
|
|
@@ -1728,7 +1735,7 @@ const switchCommand = {
|
|
|
1728
1735
|
printError("The workspace picker needs the Bun runtime and it could not be set up automatically. Pass a workspace slug instead, or install Bun and retry.");
|
|
1729
1736
|
process.exit(1);
|
|
1730
1737
|
}
|
|
1731
|
-
const { runWorkspacePicker } = await import("./boot-
|
|
1738
|
+
const { runWorkspacePicker } = await import("./boot-B-AxTj84.mjs");
|
|
1732
1739
|
await runWorkspacePicker(session);
|
|
1733
1740
|
return;
|
|
1734
1741
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { D as getReviewStateDir,
|
|
2
|
+
import { B as saveTheme, D as getReviewStateDir, L as resolveWebUrl, O as getSavedTheme, S as DEFAULT_APP_URL, T as getConfigPath, _ as setActiveWorkspace, a as noColorRequested, c as themeMode, f as themesForMode, g as listWorkspaces, i as monoTheme, l as themeModeFromColorFgBg, m as getActiveWorkspaceId, n as applyTheme, o as theme, r as findTheme, s as themeForMode, t as DEFAULT_THEME_ID, u as themeVersion, x as DEFAULT_API_URL } from "./theme-C_Fqxi-U.mjs";
|
|
3
3
|
import { n as createRestClient, r as errorDetail, t as HttpError } from "./rest-DTlkPko_.mjs";
|
|
4
4
|
import { n as isRecord, t as errorMessage } from "./util-z9Pne47f.mjs";
|
|
5
5
|
import { c as cardActionErrorMessage, d as reconcileMaskedInput, f as resolveConnectUrl, i as resolveAgent, l as parseExternalOauthConnectParams, p as parseConnectCard, s as MASK_CHAR, u as parseOauthConnectParams } from "./print-BhfjRrxI.mjs";
|
|
@@ -3637,6 +3637,198 @@ function mapToolState(raw) {
|
|
|
3637
3637
|
}
|
|
3638
3638
|
}
|
|
3639
3639
|
|
|
3640
|
+
//#endregion
|
|
3641
|
+
//#region src/chat/tui/chat/todos.ts
|
|
3642
|
+
const todoItemSchema = z.object({
|
|
3643
|
+
id: z.string(),
|
|
3644
|
+
content: z.string(),
|
|
3645
|
+
status: z.enum([
|
|
3646
|
+
"pending",
|
|
3647
|
+
"in_progress",
|
|
3648
|
+
"completed",
|
|
3649
|
+
"failed"
|
|
3650
|
+
]),
|
|
3651
|
+
position: z.number()
|
|
3652
|
+
});
|
|
3653
|
+
const todosPayloadSchema = z.object({ todos: z.array(todoItemSchema) });
|
|
3654
|
+
/**
|
|
3655
|
+
* The todos carried by a `data-anyone-todos-updated` chunk/part `data`
|
|
3656
|
+
* payload, or null when the payload isn't the documented shape. Used for both
|
|
3657
|
+
* the live stream (reducer-adjacent, in chat.tsx) and history parts, which
|
|
3658
|
+
* carry the identical `{ data: { todos } }` shape.
|
|
3659
|
+
*/
|
|
3660
|
+
function todosFromPayload(data) {
|
|
3661
|
+
const parsed = todosPayloadSchema.safeParse(data);
|
|
3662
|
+
return parsed.success ? parsed.data.todos : null;
|
|
3663
|
+
}
|
|
3664
|
+
/**
|
|
3665
|
+
* The conversation's current todo list from loaded history: the newest
|
|
3666
|
+
* `data-anyone-todos-updated` part in the transcript. Every `platform todo
|
|
3667
|
+
* write` appends a fresh snapshot rather than editing one in place, so the
|
|
3668
|
+
* last part wins and earlier ones are history — matching the web client's
|
|
3669
|
+
* `latestTodoSnapshot`.
|
|
3670
|
+
*
|
|
3671
|
+
* Returns null when the agent never wrote a list, and when the newest snapshot
|
|
3672
|
+
* is empty: `platform todo dismiss` writes an empty list, so an empty newest
|
|
3673
|
+
* snapshot means "the plan is gone", not "fall back to the previous one".
|
|
3674
|
+
*/
|
|
3675
|
+
function latestTodosFromHistory(messages) {
|
|
3676
|
+
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
3677
|
+
const parts = messages[i]?.parts ?? [];
|
|
3678
|
+
for (let j = parts.length - 1; j >= 0; j -= 1) {
|
|
3679
|
+
const part = parts[j];
|
|
3680
|
+
if (!part || part.type !== "data-anyone-todos-updated") continue;
|
|
3681
|
+
const data = isRecord(part) && "data" in part && isRecord(part.data) ? part.data : null;
|
|
3682
|
+
const todos = data ? todosFromPayload(data) : null;
|
|
3683
|
+
if (!todos) return null;
|
|
3684
|
+
return todos.length > 0 ? todos : null;
|
|
3685
|
+
}
|
|
3686
|
+
}
|
|
3687
|
+
return null;
|
|
3688
|
+
}
|
|
3689
|
+
/** How many todo rows the card shows before it caps the tail with "+N more". */
|
|
3690
|
+
const MAX_VISIBLE_ROWS = 6;
|
|
3691
|
+
function windowTodos(todos, cap = MAX_VISIBLE_ROWS) {
|
|
3692
|
+
const sorted = [...todos].sort((a, b) => a.position - b.position);
|
|
3693
|
+
let leadingDone = 0;
|
|
3694
|
+
while (leadingDone < sorted.length && sorted[leadingDone]?.status === "completed") leadingDone += 1;
|
|
3695
|
+
const head = leadingDone >= 2 || sorted.length > cap ? leadingDone : 0;
|
|
3696
|
+
const rest = sorted.slice(head);
|
|
3697
|
+
const rowBudget = Math.max(1, head > 0 ? cap - 1 : cap);
|
|
3698
|
+
if (rest.length <= rowBudget) return {
|
|
3699
|
+
doneSummary: head,
|
|
3700
|
+
rows: rest,
|
|
3701
|
+
moreCount: 0
|
|
3702
|
+
};
|
|
3703
|
+
const windowSize = Math.max(1, rowBudget - 1);
|
|
3704
|
+
const shown = rest.slice(0, windowSize);
|
|
3705
|
+
return {
|
|
3706
|
+
doneSummary: head,
|
|
3707
|
+
rows: shown,
|
|
3708
|
+
moreCount: rest.length - shown.length
|
|
3709
|
+
};
|
|
3710
|
+
}
|
|
3711
|
+
/**
|
|
3712
|
+
* Total terminal rows the card occupies for `todos`, so the chat screen can
|
|
3713
|
+
* reserve exactly this much and keep the composer pinned. Header + optional
|
|
3714
|
+
* done-summary line + rendered rows + optional "+N more" tail + top margin.
|
|
3715
|
+
* Must track `windowTodos` and `TodoCardView`'s layout.
|
|
3716
|
+
*/
|
|
3717
|
+
function todoCardHeight(todos) {
|
|
3718
|
+
if (todos.length === 0) return 0;
|
|
3719
|
+
const w = windowTodos(todos);
|
|
3720
|
+
const summaryRow = w.doneSummary > 0 ? 1 : 0;
|
|
3721
|
+
const moreRow = w.moreCount > 0 ? 1 : 0;
|
|
3722
|
+
return 1 + summaryRow + w.rows.length + moreRow + 1;
|
|
3723
|
+
}
|
|
3724
|
+
|
|
3725
|
+
//#endregion
|
|
3726
|
+
//#region src/chat/tui/chat/todo-card.tsx
|
|
3727
|
+
function glyph(status) {
|
|
3728
|
+
switch (status) {
|
|
3729
|
+
case "completed": return {
|
|
3730
|
+
char: "✔",
|
|
3731
|
+
fg: theme.success
|
|
3732
|
+
};
|
|
3733
|
+
case "in_progress": return {
|
|
3734
|
+
char: "▶",
|
|
3735
|
+
fg: theme.accent
|
|
3736
|
+
};
|
|
3737
|
+
case "failed": return {
|
|
3738
|
+
char: "✗",
|
|
3739
|
+
fg: theme.error
|
|
3740
|
+
};
|
|
3741
|
+
default: return {
|
|
3742
|
+
char: "○",
|
|
3743
|
+
fg: theme.dim
|
|
3744
|
+
};
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
function rowColor(status) {
|
|
3748
|
+
switch (status) {
|
|
3749
|
+
case "completed": return theme.dim;
|
|
3750
|
+
case "in_progress": return theme.fg;
|
|
3751
|
+
case "failed": return theme.muted;
|
|
3752
|
+
default: return theme.muted;
|
|
3753
|
+
}
|
|
3754
|
+
}
|
|
3755
|
+
/**
|
|
3756
|
+
* The agent's live todo list, pinned just above the composer — the TUI analog
|
|
3757
|
+
* of the web chat's todo card. Rendered only once the agent has actually
|
|
3758
|
+
* written a list (`platform todo write`) and until it clears one, so a
|
|
3759
|
+
* conversation the agent never wrote todos for grows no chrome for it.
|
|
3760
|
+
*
|
|
3761
|
+
* The card can't scroll like the web card, so it windows (see `windowTodos`):
|
|
3762
|
+
* a leading run of completed todos folds into a `✔ N done` line, the active
|
|
3763
|
+
* and upcoming rows fill the rest, and a `+N more` tail counts pending work
|
|
3764
|
+
* past the cap. The window follows the in-progress row down as todos complete.
|
|
3765
|
+
*
|
|
3766
|
+
* `isActive` dims the header while the run is idle: an in-progress row only
|
|
3767
|
+
* reads as "happening now" when a run is actually producing output.
|
|
3768
|
+
*/
|
|
3769
|
+
function TodoCardView({ todos, isActive }) {
|
|
3770
|
+
if (todos.length === 0) return null;
|
|
3771
|
+
const completed = todos.filter((t) => t.status === "completed").length;
|
|
3772
|
+
const { doneSummary, rows, moreCount } = windowTodos(todos);
|
|
3773
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
3774
|
+
style: {
|
|
3775
|
+
flexDirection: "column",
|
|
3776
|
+
flexShrink: 0,
|
|
3777
|
+
marginTop: 1,
|
|
3778
|
+
paddingLeft: 1,
|
|
3779
|
+
paddingRight: 1
|
|
3780
|
+
},
|
|
3781
|
+
children: [
|
|
3782
|
+
/* @__PURE__ */ jsxs("box", {
|
|
3783
|
+
style: { flexDirection: "row" },
|
|
3784
|
+
children: [/* @__PURE__ */ jsx("text", {
|
|
3785
|
+
fg: isActive ? theme.accent : theme.muted,
|
|
3786
|
+
children: /* @__PURE__ */ jsx("b", { children: "Plan" })
|
|
3787
|
+
}), /* @__PURE__ */ jsxs("text", {
|
|
3788
|
+
fg: theme.dim,
|
|
3789
|
+
children: [
|
|
3790
|
+
" ",
|
|
3791
|
+
completed,
|
|
3792
|
+
"/",
|
|
3793
|
+
todos.length
|
|
3794
|
+
]
|
|
3795
|
+
})]
|
|
3796
|
+
}),
|
|
3797
|
+
doneSummary > 0 ? /* @__PURE__ */ jsxs("box", {
|
|
3798
|
+
style: { flexDirection: "row" },
|
|
3799
|
+
children: [/* @__PURE__ */ jsx("text", {
|
|
3800
|
+
fg: theme.success,
|
|
3801
|
+
children: "✔ "
|
|
3802
|
+
}), /* @__PURE__ */ jsxs("text", {
|
|
3803
|
+
fg: theme.dim,
|
|
3804
|
+
children: [doneSummary, " done"]
|
|
3805
|
+
})]
|
|
3806
|
+
}) : null,
|
|
3807
|
+
rows.map((todo) => {
|
|
3808
|
+
const g = glyph(todo.status);
|
|
3809
|
+
return /* @__PURE__ */ jsxs("box", {
|
|
3810
|
+
style: { flexDirection: "row" },
|
|
3811
|
+
children: [/* @__PURE__ */ jsxs("text", {
|
|
3812
|
+
fg: g.fg,
|
|
3813
|
+
children: [g.char, " "]
|
|
3814
|
+
}), /* @__PURE__ */ jsx("text", {
|
|
3815
|
+
fg: rowColor(todo.status),
|
|
3816
|
+
children: todo.content
|
|
3817
|
+
})]
|
|
3818
|
+
}, todo.id);
|
|
3819
|
+
}),
|
|
3820
|
+
moreCount > 0 ? /* @__PURE__ */ jsxs("text", {
|
|
3821
|
+
fg: theme.dim,
|
|
3822
|
+
children: [
|
|
3823
|
+
" … +",
|
|
3824
|
+
moreCount,
|
|
3825
|
+
" more"
|
|
3826
|
+
]
|
|
3827
|
+
}) : null
|
|
3828
|
+
]
|
|
3829
|
+
});
|
|
3830
|
+
}
|
|
3831
|
+
|
|
3640
3832
|
//#endregion
|
|
3641
3833
|
//#region src/chat/tui/chat/footer-hints.ts
|
|
3642
3834
|
/** Build the chat footer copy independently of its colors and layout. */
|
|
@@ -7481,6 +7673,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
7481
7673
|
const agentHost = useAgentHost();
|
|
7482
7674
|
const [conversationId, setConversationId] = useState(initialConversationId);
|
|
7483
7675
|
const [items, setItems] = useState([]);
|
|
7676
|
+
const [todos, setTodos] = useState(null);
|
|
7484
7677
|
const [historyLoaded, setHistoryLoaded] = useState(initialConversationId === null);
|
|
7485
7678
|
const [input, setInput] = useState("");
|
|
7486
7679
|
const [run, setRun] = useState({ kind: "idle" });
|
|
@@ -7564,7 +7757,9 @@ function ChatScreen({ agent, conversation }) {
|
|
|
7564
7757
|
const chatHeight = bodyHeight;
|
|
7565
7758
|
const inlineReviewHeight = reviewOpen && reviewPlacement === "below" ? reviewHeight : 0;
|
|
7566
7759
|
const menuRows = menuOpen ? completionMenuHeight(menuCommands.length) : 0;
|
|
7567
|
-
const
|
|
7760
|
+
const todoCardVisible = !credPrompt && !!todos && todos.length > 0;
|
|
7761
|
+
const todoRows = todoCardVisible ? todoCardHeight(todos) : 0;
|
|
7762
|
+
const scrollHeight = Math.max(3, chatHeight - 2 - bottomBoxHeight - pendingRows - inlineReviewHeight - menuRows - todoRows);
|
|
7568
7763
|
useEffect(() => {
|
|
7569
7764
|
if (reviewOpen && reviewFocused) {
|
|
7570
7765
|
const composer = composerRef.current;
|
|
@@ -7612,6 +7807,7 @@ function ChatScreen({ agent, conversation }) {
|
|
|
7612
7807
|
text: recapText
|
|
7613
7808
|
});
|
|
7614
7809
|
setItems(loaded);
|
|
7810
|
+
setTodos(latestTodosFromHistory(messages));
|
|
7615
7811
|
} catch (err) {
|
|
7616
7812
|
if (cancelled) return;
|
|
7617
7813
|
setItems((prev) => [...prev, {
|
|
@@ -7644,6 +7840,11 @@ function ChatScreen({ agent, conversation }) {
|
|
|
7644
7840
|
onEvent: (event) => {
|
|
7645
7841
|
if (event.kind === "chunk") {
|
|
7646
7842
|
responsePreview.addChunk(event.chunk);
|
|
7843
|
+
if (event.chunk.type === "data-anyone-todos-updated") {
|
|
7844
|
+
const next = todosFromPayload(event.chunk.data);
|
|
7845
|
+
if (next) setTodos(next.length > 0 ? next : null);
|
|
7846
|
+
return;
|
|
7847
|
+
}
|
|
7647
7848
|
setItems((prev) => applyChunk(prev, event.chunk, streamAgentName));
|
|
7648
7849
|
return;
|
|
7649
7850
|
}
|
|
@@ -8751,6 +8952,10 @@ function ChatScreen({ agent, conversation }) {
|
|
|
8751
8952
|
}) }), run.kind !== "idle" ? /* @__PURE__ */ jsx(ActivityRow, { label: run.kind === "sending" ? "sending" : "working" }) : null]
|
|
8752
8953
|
})
|
|
8753
8954
|
}),
|
|
8955
|
+
todoCardVisible && todos ? /* @__PURE__ */ jsx(TodoCardView, {
|
|
8956
|
+
todos,
|
|
8957
|
+
isActive: run.kind !== "idle"
|
|
8958
|
+
}) : null,
|
|
8754
8959
|
pendingVisible ? /* @__PURE__ */ jsxs("box", {
|
|
8755
8960
|
style: {
|
|
8756
8961
|
paddingLeft: 1,
|
|
@@ -77,13 +77,17 @@ function resolveManagementAuth(opts) {
|
|
|
77
77
|
if (session.isOk()) return ok({
|
|
78
78
|
token: session.value.sessionToken,
|
|
79
79
|
apiUrl: session.value.appUrl,
|
|
80
|
-
kind: "session"
|
|
80
|
+
kind: "session",
|
|
81
|
+
pinnedWorkspaceName: null
|
|
81
82
|
});
|
|
82
|
-
const
|
|
83
|
+
const envKey = process.env["SKYDIVE_API_KEY"];
|
|
84
|
+
const storedKey = store.get("apiKey");
|
|
85
|
+
const apiKey = envKey ?? storedKey;
|
|
83
86
|
if (apiKey) return ok({
|
|
84
87
|
token: apiKey,
|
|
85
88
|
apiUrl: process.env["SKYDIVE_API_URL"] ?? opts.apiUrl ?? store.get("apiUrl") ?? DEFAULT_API_URL,
|
|
86
|
-
kind: "api-key"
|
|
89
|
+
kind: "api-key",
|
|
90
|
+
pinnedWorkspaceName: !envKey && storedKey ? store.get("apiKeyWorkspaceName") ?? null : null
|
|
87
91
|
});
|
|
88
92
|
return err({ message: "Not authenticated. Run `skydive auth login`." });
|
|
89
93
|
}
|
|
@@ -92,11 +96,17 @@ function saveConfig(config) {
|
|
|
92
96
|
store.set("apiUrl", config.apiUrl);
|
|
93
97
|
if (config.apiKeyId) store.set("apiKeyId", config.apiKeyId);
|
|
94
98
|
else store.delete("apiKeyId");
|
|
99
|
+
if (config.workspaceName) store.set("apiKeyWorkspaceName", config.workspaceName);
|
|
100
|
+
else store.delete("apiKeyWorkspaceName");
|
|
95
101
|
}
|
|
96
102
|
/** Server-side id of the auto-minted key, if login minted one. */
|
|
97
103
|
function getStoredApiKeyId() {
|
|
98
104
|
return store.get("apiKeyId") ?? null;
|
|
99
105
|
}
|
|
106
|
+
/** Workspace the auto-minted key is pinned to, if login recorded one. */
|
|
107
|
+
function getStoredApiKeyWorkspaceName() {
|
|
108
|
+
return store.get("apiKeyWorkspaceName") ?? null;
|
|
109
|
+
}
|
|
100
110
|
function deleteConfig() {
|
|
101
111
|
store.clear();
|
|
102
112
|
}
|
|
@@ -971,4 +981,4 @@ function applyTheme(def) {
|
|
|
971
981
|
}
|
|
972
982
|
|
|
973
983
|
//#endregion
|
|
974
|
-
export { getStoredApiKeyId as A, DEFAULT_WEB_URL as C, getReviewStateDir as D, getPromptHistoryPath as E,
|
|
984
|
+
export { getStoredApiKeyId as A, saveTheme as B, DEFAULT_WEB_URL as C, getReviewStateDir as D, getPromptHistoryPath as E, resolveManagementAuth as F, resolveSession as I, resolveWebUrl as L, getUpdateCheckDisabled as M, resolveAppUrl as N, getSavedTheme as O, resolveConfig as P, saveConfig as R, DEFAULT_APP_URL as S, getConfigPath as T, setActiveWorkspace as _, noColorRequested as a, API_KEY_PREFIX as b, themeMode as c, themes as d, themesForMode as f, listWorkspaces as g, getSessionIdentity as h, monoTheme as i, getStoredApiKeyWorkspaceName as j, getShareMachineDefault as k, themeModeFromColorFgBg as l, getActiveWorkspaceId as m, applyTheme as n, theme as o, ensureActiveOrganization as p, findTheme as r, themeForMode as s, DEFAULT_THEME_ID as t, themeVersion as u, API_KEYS_URL as v, deleteConfig as w, DEFAULT_API_URL as x, API_KEY_FAMILY_PREFIX as y, saveSession as z };
|