@openagents-org/agent-launcher 0.2.161 → 0.2.163
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/icons/pi.svg +1 -0
- package/package.json +1 -1
- package/registry.json +144 -0
- package/src/adapters/index.js +4 -1
- package/src/adapters/pi-launcher-provider.mjs +116 -0
- package/src/adapters/pi-stream.js +922 -0
- package/src/adapters/pi.js +1492 -0
- package/src/adapters/workspace-prompt.js +21 -0
- package/src/daemon.js +37 -2
- package/src/installer.js +20 -4
- package/src/workspace-client.js +5 -4
|
@@ -869,6 +869,26 @@ function buildOpenCodeSystemPrompt({ agentName, workspaceId, channelName, endpoi
|
|
|
869
869
|
return identity + directive + '\n' + collab + '\n' + modePrompt + '\n' + api + '\n' + buildGuardrails();
|
|
870
870
|
}
|
|
871
871
|
|
|
872
|
+
/**
|
|
873
|
+
* Build the system prompt appended to Pi's own prompt (`--append-system-prompt`).
|
|
874
|
+
*
|
|
875
|
+
* Shape mirrors buildOpenCodeSystemPrompt: identity + browser directive +
|
|
876
|
+
* collaboration + mode + the REST "skills" block, because Pi is NOT wired to an
|
|
877
|
+
* MCP server (only the Claude adapter uses --mcp-config). Pi reaches workspace
|
|
878
|
+
* APIs through its built-in `bash` tool + curl, exactly like OpenCode/Amp.
|
|
879
|
+
*
|
|
880
|
+
* NOTE: this is APPENDED, never passed via `--system-prompt` — that flag would
|
|
881
|
+
* REPLACE Pi's own coding-assistant prompt and strip its tool instructions.
|
|
882
|
+
*/
|
|
883
|
+
function buildPiSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules, browserEnabled = false }) {
|
|
884
|
+
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, mode, 'skills');
|
|
885
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
886
|
+
const collab = buildCollaborationPrompt('skills', workspaceSkillName(agentName));
|
|
887
|
+
const modePrompt = buildModePrompt(mode);
|
|
888
|
+
const api = buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channelName, disabledModules, mode });
|
|
889
|
+
return identity + directive + '\n' + collab + '\n' + modePrompt + '\n' + api + '\n' + buildGuardrails();
|
|
890
|
+
}
|
|
891
|
+
|
|
872
892
|
/**
|
|
873
893
|
* Build workspace skill markdown for OpenCode (written to .opencode/skills/).
|
|
874
894
|
*/
|
|
@@ -972,6 +992,7 @@ module.exports = {
|
|
|
972
992
|
buildOpenclawSkillMd,
|
|
973
993
|
buildOpenCodeSystemPrompt,
|
|
974
994
|
buildOpenCodeSkillMd,
|
|
995
|
+
buildPiSystemPrompt,
|
|
975
996
|
buildClaudeSkillMd,
|
|
976
997
|
buildCursorSkillMd,
|
|
977
998
|
};
|
package/src/daemon.js
CHANGED
|
@@ -51,7 +51,7 @@ class Daemon {
|
|
|
51
51
|
if (!this._nodeClient) {
|
|
52
52
|
this._nodeClient = new WorkspaceClient(n.endpoint);
|
|
53
53
|
}
|
|
54
|
-
const info = { ...nodeCfg.gatherDeviceInfo(), agents: this._buildRoster(), runtimes: this._runtimes };
|
|
54
|
+
const info = { ...nodeCfg.gatherDeviceInfo(), agents: this._buildRoster(), runtimes: this._runtimes, fs: this._buildFs() };
|
|
55
55
|
const resp = await this._nodeClient.nodeHeartbeat(n.node_id, n.token, info);
|
|
56
56
|
// The heartbeat response is our push channel: run any queued remote
|
|
57
57
|
// agent-management commands the workspace enqueued for this node. Fire
|
|
@@ -100,6 +100,36 @@ class Daemon {
|
|
|
100
100
|
return roster;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Filesystem hint for the working-directory picker: home + its immediate
|
|
105
|
+
* (non-hidden) subfolders, so the workspace can show real folders instantly.
|
|
106
|
+
*/
|
|
107
|
+
_buildFs() {
|
|
108
|
+
try {
|
|
109
|
+
const home = os.homedir();
|
|
110
|
+
const dirs = fs.readdirSync(home, { withFileTypes: true })
|
|
111
|
+
.filter((d) => d.isDirectory() && !d.name.startsWith('.'))
|
|
112
|
+
.map((d) => d.name)
|
|
113
|
+
.sort((a, b) => a.localeCompare(b))
|
|
114
|
+
.slice(0, 100);
|
|
115
|
+
return { home, dirs };
|
|
116
|
+
} catch {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/** List the (non-hidden) subfolders of a directory, for on-demand browsing. */
|
|
122
|
+
_listDir(dir) {
|
|
123
|
+
const target = dir && String(dir).trim() ? String(dir) : os.homedir();
|
|
124
|
+
const dirs = fs.readdirSync(target, { withFileTypes: true })
|
|
125
|
+
.filter((d) => d.isDirectory() && !d.name.startsWith('.'))
|
|
126
|
+
.map((d) => d.name)
|
|
127
|
+
.sort((a, b) => a.localeCompare(b))
|
|
128
|
+
.slice(0, 500);
|
|
129
|
+
const parent = path.dirname(target);
|
|
130
|
+
return { path: target, parent: parent === target ? null : parent, dirs };
|
|
131
|
+
}
|
|
132
|
+
|
|
103
133
|
/**
|
|
104
134
|
* Detect, for every supported agent type, whether its runtime is installed and
|
|
105
135
|
* logged-in/ready on this device. Runs `agn runtimes --json` in a CHILD process
|
|
@@ -129,6 +159,7 @@ class Daemon {
|
|
|
129
159
|
const name = (args.name || '').trim();
|
|
130
160
|
let ok = false;
|
|
131
161
|
let message = '';
|
|
162
|
+
let data = null;
|
|
132
163
|
try {
|
|
133
164
|
if (action === 'create_agent') {
|
|
134
165
|
const type = (args.type || '').trim();
|
|
@@ -192,6 +223,10 @@ class Daemon {
|
|
|
192
223
|
this._nodeHeartbeat();
|
|
193
224
|
ok = true;
|
|
194
225
|
message = `Detected ${this._runtimes.length} runtime(s)`;
|
|
226
|
+
} else if (action === 'list_dir') {
|
|
227
|
+
data = this._listDir(args.path);
|
|
228
|
+
ok = true;
|
|
229
|
+
message = `Listed ${data.dirs.length} folder(s)`;
|
|
195
230
|
} else {
|
|
196
231
|
message = `Unknown action '${action}'`;
|
|
197
232
|
}
|
|
@@ -200,7 +235,7 @@ class Daemon {
|
|
|
200
235
|
message = e.message || String(e);
|
|
201
236
|
}
|
|
202
237
|
try {
|
|
203
|
-
await this._nodeClient.nodeCommandResult(cmd.commandId, n.token, { ok, message });
|
|
238
|
+
await this._nodeClient.nodeCommandResult(cmd.commandId, n.token, { ok, message, data });
|
|
204
239
|
} catch {
|
|
205
240
|
// best-effort; the command stays 'running' if we can't report back
|
|
206
241
|
}
|
package/src/installer.js
CHANGED
|
@@ -665,7 +665,12 @@ class Installer {
|
|
|
665
665
|
const directReady = directEnv || directSaved;
|
|
666
666
|
const envAnyReady = this._hasAnyValue(process.env, checkReady.env_vars);
|
|
667
667
|
const savedAnyReady = !!(checkReady.saved_env_key && savedEnv[checkReady.saved_env_key]);
|
|
668
|
-
|
|
668
|
+
// A registry entry may require a non-empty JSON object rather than mere
|
|
669
|
+
// parseability. In that mode the stricter _evaluateCredsFile path below is
|
|
670
|
+
// authoritative; the legacy parser treats an empty {} as ready.
|
|
671
|
+
const credsReady = checkReady.creds_json_has_entries
|
|
672
|
+
? false
|
|
673
|
+
: this._checkCredsReady(checkReady);
|
|
669
674
|
// Content-free credential probes (opt-in via check_ready). Detect agents
|
|
670
675
|
// whose sign-in is a local OAuth/credential FILE (e.g. Gemini's
|
|
671
676
|
// ~/.gemini/oauth_creds.json) or a service-account file path, WITHOUT
|
|
@@ -792,9 +797,12 @@ class Installer {
|
|
|
792
797
|
}
|
|
793
798
|
|
|
794
799
|
/**
|
|
795
|
-
* Existence/readability of a credential file
|
|
796
|
-
*
|
|
797
|
-
*
|
|
800
|
+
* Existence/readability of a credential file. Returns 'present' |
|
|
801
|
+
* 'unreadable' | 'absent'. A non-empty readable file (or a non-empty
|
|
802
|
+
* directory) is normally 'present'. Entries with creds_json_has_entries
|
|
803
|
+
* additionally require at least one top-level JSON entry, allowing an empty
|
|
804
|
+
* auto-created {} file to remain signed out. Credential values are never
|
|
805
|
+
* logged or returned.
|
|
798
806
|
* a path that exists but cannot be statted/read is 'unreadable'. Resolves "~"
|
|
799
807
|
* against the running user's home (the daemon/launcher user), not the
|
|
800
808
|
* renderer/browser user.
|
|
@@ -809,6 +817,14 @@ class Installer {
|
|
|
809
817
|
const st = fs.statSync(p);
|
|
810
818
|
if (st.isDirectory()) return fs.readdirSync(p).length > 0 ? 'present' : 'absent';
|
|
811
819
|
fs.accessSync(p, fs.constants.R_OK);
|
|
820
|
+
if (checkReady.creds_json_has_entries) {
|
|
821
|
+
const parsed = JSON.parse(fs.readFileSync(p, 'utf-8'));
|
|
822
|
+
if (Array.isArray(parsed)) return parsed.length > 0 ? 'present' : 'absent';
|
|
823
|
+
if (parsed && typeof parsed === 'object') {
|
|
824
|
+
return Object.keys(parsed).length > 0 ? 'present' : 'absent';
|
|
825
|
+
}
|
|
826
|
+
return 'absent';
|
|
827
|
+
}
|
|
812
828
|
return st.size > 0 ? 'present' : 'absent';
|
|
813
829
|
} catch {
|
|
814
830
|
return 'unreadable';
|
package/src/workspace-client.js
CHANGED
|
@@ -135,6 +135,7 @@ class WorkspaceClient {
|
|
|
135
135
|
if (deviceInfo.launcherVersion) body.launcher_version = deviceInfo.launcherVersion;
|
|
136
136
|
if (Array.isArray(deviceInfo.agents)) body.agents = deviceInfo.agents;
|
|
137
137
|
if (Array.isArray(deviceInfo.runtimes)) body.runtimes = deviceInfo.runtimes;
|
|
138
|
+
if (deviceInfo.fs && typeof deviceInfo.fs === 'object') body.fs = deviceInfo.fs;
|
|
138
139
|
const data = await this._post('/v1/nodes/heartbeat', body, this._wsHeaders(token));
|
|
139
140
|
return data.data || data;
|
|
140
141
|
}
|
|
@@ -143,13 +144,13 @@ class WorkspaceClient {
|
|
|
143
144
|
* Report the outcome of a remote node command via
|
|
144
145
|
* POST /v1/nodes/commands/{id}/result (authenticated by the workspace token).
|
|
145
146
|
*/
|
|
146
|
-
async nodeCommandResult(commandId, token, { ok, message } = {}) {
|
|
147
|
-
const
|
|
147
|
+
async nodeCommandResult(commandId, token, { ok, message, data } = {}) {
|
|
148
|
+
const resp = await this._post(
|
|
148
149
|
`/v1/nodes/commands/${commandId}/result`,
|
|
149
|
-
{ ok: !!ok, message: message || null },
|
|
150
|
+
{ ok: !!ok, message: message || null, data: data ?? null },
|
|
150
151
|
this._wsHeaders(token),
|
|
151
152
|
);
|
|
152
|
-
return
|
|
153
|
+
return resp.data || resp;
|
|
153
154
|
}
|
|
154
155
|
|
|
155
156
|
/**
|