@phi-code-admin/phi-code 0.59.4 → 0.59.6
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/extensions/phi/init.ts +100 -0
- package/package.json +5 -3
- package/scripts/migrate-sessions.sh +93 -0
- package/scripts/postinstall.cjs +40 -0
package/extensions/phi/init.ts
CHANGED
|
@@ -714,4 +714,104 @@ _Edit this file to customize Phi Code's behavior for your project._
|
|
|
714
714
|
}
|
|
715
715
|
},
|
|
716
716
|
});
|
|
717
|
+
|
|
718
|
+
// ─── API Key Management Command ─────────────────────────────────
|
|
719
|
+
|
|
720
|
+
pi.registerCommand("api-key", {
|
|
721
|
+
description: "Set or view API keys (usage: /api-key set <provider> <key> | /api-key list)",
|
|
722
|
+
handler: async (args, ctx) => {
|
|
723
|
+
const parts = args.trim().split(/\s+/);
|
|
724
|
+
const action = parts[0]?.toLowerCase();
|
|
725
|
+
|
|
726
|
+
const PROVIDERS: Record<string, { envVar: string; name: string }> = {
|
|
727
|
+
alibaba: { envVar: "ALIBABA_CODING_PLAN_KEY", name: "Alibaba Coding Plan" },
|
|
728
|
+
dashscope: { envVar: "DASHSCOPE_API_KEY", name: "DashScope (Alibaba)" },
|
|
729
|
+
openai: { envVar: "OPENAI_API_KEY", name: "OpenAI" },
|
|
730
|
+
anthropic: { envVar: "ANTHROPIC_API_KEY", name: "Anthropic" },
|
|
731
|
+
google: { envVar: "GOOGLE_API_KEY", name: "Google" },
|
|
732
|
+
openrouter: { envVar: "OPENROUTER_API_KEY", name: "OpenRouter" },
|
|
733
|
+
groq: { envVar: "GROQ_API_KEY", name: "Groq" },
|
|
734
|
+
brave: { envVar: "BRAVE_API_KEY", name: "Brave Search" },
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
if (!action || action === "help") {
|
|
738
|
+
ctx.ui.notify(`**🔑 API Key Management**
|
|
739
|
+
|
|
740
|
+
Usage:
|
|
741
|
+
/api-key set <provider> <key> — Set an API key for this session
|
|
742
|
+
/api-key list — Show configured providers
|
|
743
|
+
/api-key providers — List all supported providers
|
|
744
|
+
|
|
745
|
+
Supported providers: ${Object.keys(PROVIDERS).join(", ")}
|
|
746
|
+
|
|
747
|
+
Example:
|
|
748
|
+
/api-key set alibaba sk-sp-xxx
|
|
749
|
+
/api-key set openai sk-xxx
|
|
750
|
+
|
|
751
|
+
Keys set with /api-key are active for the current session.
|
|
752
|
+
For persistence, set environment variables:
|
|
753
|
+
• Windows: setx ALIBABA_CODING_PLAN_KEY "your-key"
|
|
754
|
+
• Linux/Mac: export ALIBABA_CODING_PLAN_KEY="your-key"`, "info");
|
|
755
|
+
return;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (action === "list") {
|
|
759
|
+
let found = 0;
|
|
760
|
+
let msg = "**🔑 Configured API Keys:**\n";
|
|
761
|
+
for (const [id, info] of Object.entries(PROVIDERS)) {
|
|
762
|
+
const key = process.env[info.envVar];
|
|
763
|
+
if (key) {
|
|
764
|
+
const masked = key.substring(0, 6) + "..." + key.substring(key.length - 4);
|
|
765
|
+
msg += ` ✅ ${info.name} (${id}): ${masked}\n`;
|
|
766
|
+
found++;
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
if (found === 0) {
|
|
770
|
+
msg += " ❌ No API keys configured\n";
|
|
771
|
+
msg += "\nUse `/api-key set <provider> <key>` to add one.";
|
|
772
|
+
}
|
|
773
|
+
ctx.ui.notify(msg, "info");
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
if (action === "providers") {
|
|
778
|
+
let msg = "**Supported Providers:**\n";
|
|
779
|
+
for (const [id, info] of Object.entries(PROVIDERS)) {
|
|
780
|
+
const status = process.env[info.envVar] ? "✅" : "⬜";
|
|
781
|
+
msg += ` ${status} **${id}** — ${info.name} (${info.envVar})\n`;
|
|
782
|
+
}
|
|
783
|
+
ctx.ui.notify(msg, "info");
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
if (action === "set") {
|
|
788
|
+
const provider = parts[1]?.toLowerCase();
|
|
789
|
+
const key = parts[2];
|
|
790
|
+
|
|
791
|
+
if (!provider || !key) {
|
|
792
|
+
ctx.ui.notify("Usage: /api-key set <provider> <key>\nExample: /api-key set alibaba sk-sp-xxx", "warning");
|
|
793
|
+
return;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
const info = PROVIDERS[provider];
|
|
797
|
+
if (!info) {
|
|
798
|
+
ctx.ui.notify(`Unknown provider "${provider}". Supported: ${Object.keys(PROVIDERS).join(", ")}`, "error");
|
|
799
|
+
return;
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
// Set in current process environment
|
|
803
|
+
process.env[info.envVar] = key;
|
|
804
|
+
// Also set common aliases
|
|
805
|
+
if (provider === "alibaba") {
|
|
806
|
+
process.env.DASHSCOPE_API_KEY = key;
|
|
807
|
+
}
|
|
808
|
+
|
|
809
|
+
const masked = key.substring(0, 6) + "..." + key.substring(key.length - 4);
|
|
810
|
+
ctx.ui.notify(`✅ **${info.name}** API key set: ${masked}\n\n⚠️ Active for this session only. For persistence:\n • Windows: \`setx ${info.envVar} "${key.substring(0, 6)}..."\`\n • Linux/Mac: Add \`export ${info.envVar}="..."\` to ~/.bashrc`, "info");
|
|
811
|
+
return;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
ctx.ui.notify("Unknown action. Use: /api-key set|list|providers|help", "warning");
|
|
815
|
+
},
|
|
816
|
+
});
|
|
717
817
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@phi-code-admin/phi-code",
|
|
3
|
-
"version": "0.59.
|
|
3
|
+
"version": "0.59.6",
|
|
4
4
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"piConfig": {
|
|
@@ -29,7 +29,8 @@
|
|
|
29
29
|
"extensions",
|
|
30
30
|
"README.md",
|
|
31
31
|
"LICENSE",
|
|
32
|
-
"CHANGELOG.md"
|
|
32
|
+
"CHANGELOG.md",
|
|
33
|
+
"scripts"
|
|
33
34
|
],
|
|
34
35
|
"scripts": {
|
|
35
36
|
"clean": "shx rm -rf dist",
|
|
@@ -39,7 +40,8 @@
|
|
|
39
40
|
"copy-assets": "shx mkdir -p dist/modes/interactive/theme && shx cp src/modes/interactive/theme/*.json dist/modes/interactive/theme/ && shx mkdir -p dist/core/export-html/vendor && shx cp src/core/export-html/template.html src/core/export-html/template.css src/core/export-html/template.js dist/core/export-html/ && shx cp src/core/export-html/vendor/*.js dist/core/export-html/vendor/",
|
|
40
41
|
"copy-binary-assets": "shx cp package.json dist/ && shx cp README.md dist/ && shx cp CHANGELOG.md dist/ && shx mkdir -p dist/theme && shx cp src/modes/interactive/theme/*.json dist/theme/ && shx mkdir -p dist/export-html/vendor && shx cp src/core/export-html/template.html dist/export-html/ && shx cp src/core/export-html/vendor/*.js dist/export-html/vendor/ && shx cp -r docs dist/ && shx cp -r examples dist/ && shx cp ../../node_modules/@silvia-odwyer/photon-node/photon_rs_bg.wasm dist/",
|
|
41
42
|
"test": "vitest --run",
|
|
42
|
-
"prepublishOnly": "npm run clean && npm run build"
|
|
43
|
+
"prepublishOnly": "npm run clean && npm run build",
|
|
44
|
+
"postinstall": "node scripts/postinstall.cjs"
|
|
43
45
|
},
|
|
44
46
|
"dependencies": {
|
|
45
47
|
"@mariozechner/jiti": "^2.6.2",
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# Migrate sessions from ~/.pi/agent/*.jsonl to proper session directories.
|
|
4
|
+
# This fixes sessions created by the bug in v0.30.0 where sessions were
|
|
5
|
+
# saved to ~/.pi/agent/ instead of ~/.pi/agent/sessions/<encoded-cwd>/.
|
|
6
|
+
#
|
|
7
|
+
# Usage: ./migrate-sessions.sh [--dry-run]
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
set -e
|
|
11
|
+
|
|
12
|
+
AGENT_DIR="${PI_AGENT_DIR:-$HOME/.pi/agent}"
|
|
13
|
+
DRY_RUN=false
|
|
14
|
+
|
|
15
|
+
if [[ "$1" == "--dry-run" ]]; then
|
|
16
|
+
DRY_RUN=true
|
|
17
|
+
echo "Dry run mode - no files will be moved"
|
|
18
|
+
echo
|
|
19
|
+
fi
|
|
20
|
+
|
|
21
|
+
# Find all .jsonl files directly in agent dir (not in subdirectories)
|
|
22
|
+
shopt -s nullglob
|
|
23
|
+
files=("$AGENT_DIR"/*.jsonl)
|
|
24
|
+
shopt -u nullglob
|
|
25
|
+
|
|
26
|
+
if [[ ${#files[@]} -eq 0 ]]; then
|
|
27
|
+
echo "No session files found in $AGENT_DIR"
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
30
|
+
|
|
31
|
+
echo "Found ${#files[@]} session file(s) to migrate"
|
|
32
|
+
echo
|
|
33
|
+
|
|
34
|
+
migrated=0
|
|
35
|
+
failed=0
|
|
36
|
+
|
|
37
|
+
for file in "${files[@]}"; do
|
|
38
|
+
filename=$(basename "$file")
|
|
39
|
+
|
|
40
|
+
# Read first line and extract cwd using jq
|
|
41
|
+
if ! first_line=$(head -1 "$file" 2>/dev/null); then
|
|
42
|
+
echo "SKIP: $filename - cannot read file"
|
|
43
|
+
((failed++))
|
|
44
|
+
continue
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# Parse JSON and extract cwd
|
|
48
|
+
if ! cwd=$(echo "$first_line" | jq -r '.cwd // empty' 2>/dev/null); then
|
|
49
|
+
echo "SKIP: $filename - invalid JSON"
|
|
50
|
+
((failed++))
|
|
51
|
+
continue
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
if [[ -z "$cwd" ]]; then
|
|
55
|
+
echo "SKIP: $filename - no cwd in session header"
|
|
56
|
+
((failed++))
|
|
57
|
+
continue
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Encode cwd: remove leading slash, replace slashes with dashes, wrap with --
|
|
61
|
+
encoded=$(echo "$cwd" | sed 's|^/||' | sed 's|[/:\\]|-|g')
|
|
62
|
+
encoded="--${encoded}--"
|
|
63
|
+
|
|
64
|
+
target_dir="$AGENT_DIR/sessions/$encoded"
|
|
65
|
+
target_file="$target_dir/$filename"
|
|
66
|
+
|
|
67
|
+
if [[ -e "$target_file" ]]; then
|
|
68
|
+
echo "SKIP: $filename - target already exists"
|
|
69
|
+
((failed++))
|
|
70
|
+
continue
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
echo "MIGRATE: $filename"
|
|
74
|
+
echo " cwd: $cwd"
|
|
75
|
+
echo " to: $target_dir/"
|
|
76
|
+
|
|
77
|
+
if [[ "$DRY_RUN" == false ]]; then
|
|
78
|
+
mkdir -p "$target_dir"
|
|
79
|
+
mv "$file" "$target_file"
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
((migrated++))
|
|
83
|
+
echo
|
|
84
|
+
done
|
|
85
|
+
|
|
86
|
+
echo "---"
|
|
87
|
+
echo "Migrated: $migrated"
|
|
88
|
+
echo "Skipped: $failed"
|
|
89
|
+
|
|
90
|
+
if [[ "$DRY_RUN" == true && $migrated -gt 0 ]]; then
|
|
91
|
+
echo
|
|
92
|
+
echo "Run without --dry-run to perform the migration"
|
|
93
|
+
fi
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Post-install script: copies bundled extensions, agents, and skills
|
|
4
|
+
* to ~/.phi/agent/ so Pi discovers them automatically.
|
|
5
|
+
*/
|
|
6
|
+
const { existsSync, mkdirSync, cpSync, readdirSync } = require("fs");
|
|
7
|
+
const { join } = require("path");
|
|
8
|
+
const { homedir } = require("os");
|
|
9
|
+
|
|
10
|
+
const agentDir = join(homedir(), ".phi", "agent");
|
|
11
|
+
const packageDir = __dirname.replace(/[/\\]scripts$/, "");
|
|
12
|
+
|
|
13
|
+
const copies = [
|
|
14
|
+
{ src: "extensions/phi", dest: join(agentDir, "extensions"), label: "extensions" },
|
|
15
|
+
{ src: "agents", dest: join(agentDir, "agents"), label: "agents" },
|
|
16
|
+
{ src: "skills", dest: join(agentDir, "skills"), label: "skills" },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
for (const { src, dest, label } of copies) {
|
|
20
|
+
const srcDir = join(packageDir, src);
|
|
21
|
+
if (!existsSync(srcDir)) continue;
|
|
22
|
+
|
|
23
|
+
mkdirSync(dest, { recursive: true });
|
|
24
|
+
|
|
25
|
+
const files = readdirSync(srcDir);
|
|
26
|
+
let copied = 0;
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const srcPath = join(srcDir, file);
|
|
29
|
+
const destPath = join(dest, file);
|
|
30
|
+
try {
|
|
31
|
+
cpSync(srcPath, destPath, { recursive: true, force: true });
|
|
32
|
+
copied++;
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// Skip files that can't be copied (permissions, etc.)
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
if (copied > 0) {
|
|
38
|
+
console.log(` Φ Installed ${copied} ${label} to ${dest}`);
|
|
39
|
+
}
|
|
40
|
+
}
|