@clawling/clawchat-plugin-openclaw 2026.7.9-1 → 2026.7.10-1
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/dist/src/config.js +1 -5
- package/dist/src/liveware-sample.js +72 -19
- package/dist/src/runtime.js +1 -0
- package/package.json +1 -1
- package/src/config.ts +1 -5
- package/src/liveware-sample.ts +83 -26
- package/src/runtime.ts +1 -0
package/dist/src/config.js
CHANGED
|
@@ -353,11 +353,7 @@ export function resolveOpenclawClawlingAccount(cfg, env = process.env) {
|
|
|
353
353
|
const forwardToolCalls = typeof channel.forwardToolCalls === "boolean" ? channel.forwardToolCalls : false;
|
|
354
354
|
const richInteractions = typeof channel.richInteractions === "boolean" ? channel.richInteractions : false;
|
|
355
355
|
const awarenessNote = typeof channel.awarenessNote === "boolean" ? channel.awarenessNote : false;
|
|
356
|
-
|
|
357
|
-
// until the template distribution moves off raw.githubusercontent (rate
|
|
358
|
-
// limiting strands fresh agents). Flip the fallback back to true then.
|
|
359
|
-
// Explicit `livewareSample: true` in channel config still enables it.
|
|
360
|
-
const livewareSample = typeof channel.livewareSample === "boolean" ? channel.livewareSample : false;
|
|
356
|
+
const livewareSample = typeof channel.livewareSample === "boolean" ? channel.livewareSample : true;
|
|
361
357
|
return {
|
|
362
358
|
accountId: DEFAULT_ACCOUNT_ID,
|
|
363
359
|
name: CHANNEL_ID,
|
|
@@ -144,7 +144,10 @@ function waitForOutput(child, match, timeoutMs, label) {
|
|
|
144
144
|
}
|
|
145
145
|
export async function startSampleServer(opts) {
|
|
146
146
|
const spawnFn = opts.spawnFn ?? nodeSpawn;
|
|
147
|
-
const
|
|
147
|
+
const args = [path.join(opts.appDir, "server.mjs"), "--dir", opts.appDir, "--port", String(opts.port)];
|
|
148
|
+
if (opts.agentUserId)
|
|
149
|
+
args.push("--agent-id", opts.agentUserId);
|
|
150
|
+
const child = spawnFn(process.execPath, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
148
151
|
const line = await waitForOutput(child, (acc) => acc.split("\n").find((l) => l.trim().startsWith('{"port"')) ?? null, opts.timeoutMs ?? SERVER_START_TIMEOUT_MS, "liveware-sample server start");
|
|
149
152
|
return { child, port: JSON.parse(line).port };
|
|
150
153
|
}
|
|
@@ -169,11 +172,52 @@ export function parseTunnelPublicUrl(output) {
|
|
|
169
172
|
}
|
|
170
173
|
return null;
|
|
171
174
|
}
|
|
172
|
-
|
|
175
|
+
/**
|
|
176
|
+
* One-shot upstream registration (CLI v0.0.11+): `tunnel bind` writes the
|
|
177
|
+
* app→local-upstream mapping to the control plane, prints the binding table
|
|
178
|
+
* and exits 0. The data plane is carried by the persistent `liveware agent`
|
|
179
|
+
* daemon (see startTunnelAgent) — bind itself no longer stays running.
|
|
180
|
+
*/
|
|
181
|
+
export async function tunnelBind(opts) {
|
|
182
|
+
const execFileFn = opts.execFileFn ?? nodeExecFile;
|
|
183
|
+
const output = await new Promise((resolve, reject) => {
|
|
184
|
+
execFileFn(opts.livewarePath, ["tunnel", "bind", opts.appId, `http://127.0.0.1:${opts.port}`], { timeout: CLI_TIMEOUT_MS }, (err, out, errOut) => {
|
|
185
|
+
if (err) {
|
|
186
|
+
reject(new Error(`liveware tunnel bind failed: ${errOut || out || String(err)}`.trim()));
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
resolve(`${String(out ?? "")}\n${String(errOut ?? "")}`);
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
const url = parseTunnelPublicUrl(output);
|
|
193
|
+
if (!url)
|
|
194
|
+
throw new Error(`liveware tunnel bind: cannot parse public URL from output: ${output.slice(0, 500)}`);
|
|
195
|
+
return url;
|
|
196
|
+
}
|
|
197
|
+
const AGENT_READY_MARKER = "relay grpc control connected";
|
|
198
|
+
/**
|
|
199
|
+
* Match the `liveware agent` ready signal: the daemon logs a JSON line once
|
|
200
|
+
* its gRPC control channel to the relay is up — only then can the public URL
|
|
201
|
+
* actually reach the local upstream.
|
|
202
|
+
*/
|
|
203
|
+
export function parseAgentReady(output) {
|
|
204
|
+
for (const ln of (output ?? "").split("\n")) {
|
|
205
|
+
if (ln.includes(AGENT_READY_MARKER))
|
|
206
|
+
return ln;
|
|
207
|
+
}
|
|
208
|
+
return null;
|
|
209
|
+
}
|
|
210
|
+
/**
|
|
211
|
+
* Spawn the persistent `liveware agent` data-plane daemon (CLI v0.0.11+;
|
|
212
|
+
* replaces the long-lived `tunnel bind` / sibling tunnel-agent binary). It
|
|
213
|
+
* authenticates from the token saved by `liveware login` and serves every
|
|
214
|
+
* app bound via `tunnel bind` for that account.
|
|
215
|
+
*/
|
|
216
|
+
export async function startTunnelAgent(opts) {
|
|
173
217
|
const spawnFn = opts.spawnFn ?? nodeSpawn;
|
|
174
|
-
const child = spawnFn(opts.livewarePath, ["
|
|
175
|
-
|
|
176
|
-
return { child
|
|
218
|
+
const child = spawnFn(opts.livewarePath, ["agent"], { stdio: ["ignore", "pipe", "pipe"] });
|
|
219
|
+
await waitForOutput(child, (acc) => parseAgentReady(acc), opts.timeoutMs ?? TUNNEL_START_TIMEOUT_MS, "liveware agent start");
|
|
220
|
+
return { child };
|
|
177
221
|
}
|
|
178
222
|
export async function livewareLogin(opts) {
|
|
179
223
|
const execFileFn = opts.execFileFn ?? nodeExecFile;
|
|
@@ -250,8 +294,9 @@ export async function livewareAppCreate(opts) {
|
|
|
250
294
|
throw new Error(`liveware app create: cannot parse app id from output: ${stdout.slice(0, 500)}`);
|
|
251
295
|
return appId;
|
|
252
296
|
}
|
|
253
|
-
export const LIVEWARE_SAMPLE_INTRO_TEXT = "我给你安装了一个 liveware 演示应用「Liveware Sample
|
|
254
|
-
"
|
|
297
|
+
export const LIVEWARE_SAMPLE_INTRO_TEXT = "我给你安装了一个 liveware 演示应用「Liveware Sample」。" +
|
|
298
|
+
"入口:在我们的对话页面,点右上角的「应用」按钮(✦),在打开的面板里选名为「Liveware Sample」的应用。" +
|
|
299
|
+
"页面上有完整的使用引导,试试对我说:把标题改成 Hello Liveware。" +
|
|
255
300
|
"你在页面上点的按钮、提交的留言我也能看到,随时问我。";
|
|
256
301
|
const DEFAULT_SAMPLE_PORT = 43110;
|
|
257
302
|
const RESTART_WINDOW_MS = 30 * 60 * 1000;
|
|
@@ -384,6 +429,7 @@ export class LivewareSampleSupervisor {
|
|
|
384
429
|
return;
|
|
385
430
|
const server = await startSampleServer({
|
|
386
431
|
appDir, port: DEFAULT_SAMPLE_PORT, spawnFn: deps.spawnFn,
|
|
432
|
+
agentUserId: deps.resolveAgentUserId?.() ?? null,
|
|
387
433
|
});
|
|
388
434
|
this.serverChild = server.child;
|
|
389
435
|
if (this.bailIfStopped())
|
|
@@ -392,21 +438,24 @@ export class LivewareSampleSupervisor {
|
|
|
392
438
|
const appId = await livewareAppCreate({
|
|
393
439
|
livewarePath, name: LIVEWARE_SAMPLE_APP_NAME, execFileFn: deps.execFileFn,
|
|
394
440
|
});
|
|
395
|
-
const
|
|
396
|
-
livewarePath, appId, port: server.port,
|
|
441
|
+
const publicUrl = await tunnelBind({
|
|
442
|
+
livewarePath, appId, port: server.port, execFileFn: deps.execFileFn,
|
|
397
443
|
});
|
|
398
|
-
this.tunnelChild = tunnel.child;
|
|
399
444
|
if (this.bailIfStopped())
|
|
400
445
|
return;
|
|
401
|
-
await
|
|
446
|
+
const agent = await startTunnelAgent({ livewarePath, spawnFn: deps.spawnFn });
|
|
447
|
+
this.tunnelChild = agent.child;
|
|
448
|
+
if (this.bailIfStopped())
|
|
449
|
+
return;
|
|
450
|
+
await deps.registerApp({ name: LIVEWARE_SAMPLE_APP_NAME, appId, url: publicUrl });
|
|
402
451
|
deps.store.upsertLivewareSample({
|
|
403
452
|
platform: deps.platform, accountId: deps.accountId,
|
|
404
453
|
appId, appName: LIVEWARE_SAMPLE_APP_NAME, port: server.port,
|
|
405
|
-
publicUrl
|
|
454
|
+
publicUrl, sampleVersion: version, status: "active",
|
|
406
455
|
});
|
|
407
456
|
this.watchChildren();
|
|
408
457
|
await this.deliverIntro();
|
|
409
|
-
deps.log?.debug?.(`liveware-sample: bootstrap complete at ${
|
|
458
|
+
deps.log?.debug?.(`liveware-sample: bootstrap complete at ${publicUrl}`);
|
|
410
459
|
}
|
|
411
460
|
async relaunch(row) {
|
|
412
461
|
const { deps } = this;
|
|
@@ -452,25 +501,29 @@ export class LivewareSampleSupervisor {
|
|
|
452
501
|
return;
|
|
453
502
|
const server = await startSampleServer({
|
|
454
503
|
appDir, port: row.port || DEFAULT_SAMPLE_PORT, spawnFn: deps.spawnFn,
|
|
504
|
+
agentUserId: deps.resolveAgentUserId?.() ?? null,
|
|
455
505
|
});
|
|
456
506
|
this.serverChild = server.child;
|
|
457
507
|
if (this.bailIfStopped())
|
|
458
508
|
return;
|
|
459
|
-
const
|
|
460
|
-
livewarePath, appId: row.app_id, port: server.port,
|
|
509
|
+
const publicUrl = await tunnelBind({
|
|
510
|
+
livewarePath, appId: row.app_id, port: server.port, execFileFn: deps.execFileFn,
|
|
461
511
|
});
|
|
462
|
-
this.tunnelChild = tunnel.child;
|
|
463
512
|
if (this.bailIfStopped())
|
|
464
513
|
return;
|
|
465
|
-
|
|
514
|
+
const agent = await startTunnelAgent({ livewarePath, spawnFn: deps.spawnFn });
|
|
515
|
+
this.tunnelChild = agent.child;
|
|
516
|
+
if (this.bailIfStopped())
|
|
517
|
+
return;
|
|
518
|
+
if (publicUrl !== row.public_url) {
|
|
466
519
|
await deps.registerApp({
|
|
467
|
-
name: row.app_name, appId: row.app_id, url:
|
|
520
|
+
name: row.app_name, appId: row.app_id, url: publicUrl,
|
|
468
521
|
});
|
|
469
522
|
}
|
|
470
523
|
deps.store.upsertLivewareSample({
|
|
471
524
|
platform: deps.platform, accountId: deps.accountId,
|
|
472
525
|
appId: row.app_id, appName: row.app_name, port: server.port,
|
|
473
|
-
publicUrl
|
|
526
|
+
publicUrl, sampleVersion: version, status: "active",
|
|
474
527
|
});
|
|
475
528
|
this.watchChildren();
|
|
476
529
|
if (row.intro_sent === 0)
|
package/dist/src/runtime.js
CHANGED
|
@@ -801,6 +801,7 @@ export async function startOpenclawClawlingGateway(params) {
|
|
|
801
801
|
store: store,
|
|
802
802
|
sampleRoot: path.join(stateDirForSample ?? "", "clawchat", "liveware-sample"),
|
|
803
803
|
resolveToken: () => account.token ?? "",
|
|
804
|
+
resolveAgentUserId: () => account.userId ?? null,
|
|
804
805
|
resolveLivewarePath: () => resolveLivewarePath(stateDirForSample),
|
|
805
806
|
cliReady: livewareCliReady,
|
|
806
807
|
listApps: async () => {
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -519,12 +519,8 @@ export function resolveOpenclawClawlingAccount(
|
|
|
519
519
|
typeof channel.richInteractions === "boolean" ? channel.richInteractions : false;
|
|
520
520
|
const awarenessNote =
|
|
521
521
|
typeof channel.awarenessNote === "boolean" ? channel.awarenessNote : false;
|
|
522
|
-
// TEMPORARY default-off: liveware-sample auto-boot is disabled by default
|
|
523
|
-
// until the template distribution moves off raw.githubusercontent (rate
|
|
524
|
-
// limiting strands fresh agents). Flip the fallback back to true then.
|
|
525
|
-
// Explicit `livewareSample: true` in channel config still enables it.
|
|
526
522
|
const livewareSample =
|
|
527
|
-
typeof channel.livewareSample === "boolean" ? channel.livewareSample :
|
|
523
|
+
typeof channel.livewareSample === "boolean" ? channel.livewareSample : true;
|
|
528
524
|
|
|
529
525
|
return {
|
|
530
526
|
accountId: DEFAULT_ACCOUNT_ID,
|
package/src/liveware-sample.ts
CHANGED
|
@@ -183,15 +183,14 @@ function waitForOutput(
|
|
|
183
183
|
export async function startSampleServer(opts: {
|
|
184
184
|
appDir: string;
|
|
185
185
|
port: number;
|
|
186
|
+
agentUserId?: string | null;
|
|
186
187
|
spawnFn?: SpawnLike;
|
|
187
188
|
timeoutMs?: number;
|
|
188
189
|
}): Promise<{ child: ChildProcess; port: number }> {
|
|
189
190
|
const spawnFn = opts.spawnFn ?? nodeSpawn;
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
{ stdio: ["ignore", "pipe", "pipe"] },
|
|
194
|
-
);
|
|
191
|
+
const args = [path.join(opts.appDir, "server.mjs"), "--dir", opts.appDir, "--port", String(opts.port)];
|
|
192
|
+
if (opts.agentUserId) args.push("--agent-id", opts.agentUserId);
|
|
193
|
+
const child = spawnFn(process.execPath, args, { stdio: ["ignore", "pipe", "pipe"] });
|
|
195
194
|
const line = await waitForOutput(
|
|
196
195
|
child,
|
|
197
196
|
(acc) => acc.split("\n").find((l) => l.trim().startsWith('{"port"')) ?? null,
|
|
@@ -224,26 +223,73 @@ export function parseTunnelPublicUrl(output: string): string | null {
|
|
|
224
223
|
return null;
|
|
225
224
|
}
|
|
226
225
|
|
|
227
|
-
|
|
226
|
+
/**
|
|
227
|
+
* One-shot upstream registration (CLI v0.0.11+): `tunnel bind` writes the
|
|
228
|
+
* app→local-upstream mapping to the control plane, prints the binding table
|
|
229
|
+
* and exits 0. The data plane is carried by the persistent `liveware agent`
|
|
230
|
+
* daemon (see startTunnelAgent) — bind itself no longer stays running.
|
|
231
|
+
*/
|
|
232
|
+
export async function tunnelBind(opts: {
|
|
228
233
|
livewarePath: string;
|
|
229
234
|
appId: string;
|
|
230
235
|
port: number;
|
|
236
|
+
execFileFn?: ExecFileLike;
|
|
237
|
+
}): Promise<string> {
|
|
238
|
+
const execFileFn = opts.execFileFn ?? nodeExecFile;
|
|
239
|
+
const output = await new Promise<string>((resolve, reject) => {
|
|
240
|
+
execFileFn(
|
|
241
|
+
opts.livewarePath,
|
|
242
|
+
["tunnel", "bind", opts.appId, `http://127.0.0.1:${opts.port}`],
|
|
243
|
+
{ timeout: CLI_TIMEOUT_MS },
|
|
244
|
+
(err, out, errOut) => {
|
|
245
|
+
if (err) { reject(new Error(`liveware tunnel bind failed: ${errOut || out || String(err)}`.trim())); return; }
|
|
246
|
+
resolve(`${String(out ?? "")}\n${String(errOut ?? "")}`);
|
|
247
|
+
},
|
|
248
|
+
);
|
|
249
|
+
});
|
|
250
|
+
const url = parseTunnelPublicUrl(output);
|
|
251
|
+
if (!url) throw new Error(`liveware tunnel bind: cannot parse public URL from output: ${output.slice(0, 500)}`);
|
|
252
|
+
return url;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const AGENT_READY_MARKER = "relay grpc control connected";
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Match the `liveware agent` ready signal: the daemon logs a JSON line once
|
|
259
|
+
* its gRPC control channel to the relay is up — only then can the public URL
|
|
260
|
+
* actually reach the local upstream.
|
|
261
|
+
*/
|
|
262
|
+
export function parseAgentReady(output: string): string | null {
|
|
263
|
+
for (const ln of (output ?? "").split("\n")) {
|
|
264
|
+
if (ln.includes(AGENT_READY_MARKER)) return ln;
|
|
265
|
+
}
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Spawn the persistent `liveware agent` data-plane daemon (CLI v0.0.11+;
|
|
271
|
+
* replaces the long-lived `tunnel bind` / sibling tunnel-agent binary). It
|
|
272
|
+
* authenticates from the token saved by `liveware login` and serves every
|
|
273
|
+
* app bound via `tunnel bind` for that account.
|
|
274
|
+
*/
|
|
275
|
+
export async function startTunnelAgent(opts: {
|
|
276
|
+
livewarePath: string;
|
|
231
277
|
spawnFn?: SpawnLike;
|
|
232
278
|
timeoutMs?: number;
|
|
233
|
-
}): Promise<{ child: ChildProcess
|
|
279
|
+
}): Promise<{ child: ChildProcess }> {
|
|
234
280
|
const spawnFn = opts.spawnFn ?? nodeSpawn;
|
|
235
281
|
const child = spawnFn(
|
|
236
282
|
opts.livewarePath,
|
|
237
|
-
["
|
|
283
|
+
["agent"],
|
|
238
284
|
{ stdio: ["ignore", "pipe", "pipe"] },
|
|
239
285
|
);
|
|
240
|
-
|
|
286
|
+
await waitForOutput(
|
|
241
287
|
child,
|
|
242
|
-
(acc) =>
|
|
288
|
+
(acc) => parseAgentReady(acc),
|
|
243
289
|
opts.timeoutMs ?? TUNNEL_START_TIMEOUT_MS,
|
|
244
|
-
"liveware
|
|
290
|
+
"liveware agent start",
|
|
245
291
|
);
|
|
246
|
-
return { child
|
|
292
|
+
return { child };
|
|
247
293
|
}
|
|
248
294
|
|
|
249
295
|
export async function livewareLogin(opts: {
|
|
@@ -349,6 +395,10 @@ export type LivewareSampleSupervisorDeps = {
|
|
|
349
395
|
sampleRoot: string;
|
|
350
396
|
resolveToken: () => string;
|
|
351
397
|
resolveLivewarePath: () => string | null;
|
|
398
|
+
/** The agent's own ClawChat user id (`usr_…`) — minted into the sample
|
|
399
|
+
* page's back-to-chat deep link. Read at spawn time; null/absent → the
|
|
400
|
+
* page renders its text fallback instead of the link. */
|
|
401
|
+
resolveAgentUserId?: () => string | null;
|
|
352
402
|
/** Settles when the liveware CLI self-download has finished (either way).
|
|
353
403
|
* start() awaits it before the CLI gate so a first boot doesn't lose the
|
|
354
404
|
* race against ensureLivewareCli and silently skip forever. */
|
|
@@ -365,8 +415,9 @@ export type LivewareSampleSupervisorDeps = {
|
|
|
365
415
|
};
|
|
366
416
|
|
|
367
417
|
export const LIVEWARE_SAMPLE_INTRO_TEXT =
|
|
368
|
-
"我给你安装了一个 liveware 演示应用「Liveware Sample
|
|
369
|
-
"
|
|
418
|
+
"我给你安装了一个 liveware 演示应用「Liveware Sample」。" +
|
|
419
|
+
"入口:在我们的对话页面,点右上角的「应用」按钮(✦),在打开的面板里选名为「Liveware Sample」的应用。" +
|
|
420
|
+
"页面上有完整的使用引导,试试对我说:把标题改成 Hello Liveware。" +
|
|
370
421
|
"你在页面上点的按钮、提交的留言我也能看到,随时问我。";
|
|
371
422
|
|
|
372
423
|
const DEFAULT_SAMPLE_PORT = 43110;
|
|
@@ -492,6 +543,7 @@ export class LivewareSampleSupervisor {
|
|
|
492
543
|
if (this.bailIfStopped()) return;
|
|
493
544
|
const server = await startSampleServer({
|
|
494
545
|
appDir, port: DEFAULT_SAMPLE_PORT, spawnFn: deps.spawnFn,
|
|
546
|
+
agentUserId: deps.resolveAgentUserId?.() ?? null,
|
|
495
547
|
});
|
|
496
548
|
this.serverChild = server.child;
|
|
497
549
|
if (this.bailIfStopped()) return;
|
|
@@ -499,20 +551,22 @@ export class LivewareSampleSupervisor {
|
|
|
499
551
|
const appId = await livewareAppCreate({
|
|
500
552
|
livewarePath, name: LIVEWARE_SAMPLE_APP_NAME, execFileFn: deps.execFileFn,
|
|
501
553
|
});
|
|
502
|
-
const
|
|
503
|
-
livewarePath, appId, port: server.port,
|
|
554
|
+
const publicUrl = await tunnelBind({
|
|
555
|
+
livewarePath, appId, port: server.port, execFileFn: deps.execFileFn,
|
|
504
556
|
});
|
|
505
|
-
this.tunnelChild = tunnel.child;
|
|
506
557
|
if (this.bailIfStopped()) return;
|
|
507
|
-
await
|
|
558
|
+
const agent = await startTunnelAgent({ livewarePath, spawnFn: deps.spawnFn });
|
|
559
|
+
this.tunnelChild = agent.child;
|
|
560
|
+
if (this.bailIfStopped()) return;
|
|
561
|
+
await deps.registerApp({ name: LIVEWARE_SAMPLE_APP_NAME, appId, url: publicUrl });
|
|
508
562
|
deps.store.upsertLivewareSample({
|
|
509
563
|
platform: deps.platform, accountId: deps.accountId,
|
|
510
564
|
appId, appName: LIVEWARE_SAMPLE_APP_NAME, port: server.port,
|
|
511
|
-
publicUrl
|
|
565
|
+
publicUrl, sampleVersion: version, status: "active",
|
|
512
566
|
});
|
|
513
567
|
this.watchChildren();
|
|
514
568
|
await this.deliverIntro();
|
|
515
|
-
deps.log?.debug?.(`liveware-sample: bootstrap complete at ${
|
|
569
|
+
deps.log?.debug?.(`liveware-sample: bootstrap complete at ${publicUrl}`);
|
|
516
570
|
}
|
|
517
571
|
|
|
518
572
|
private async relaunch(row: LivewareSampleRow): Promise<void> {
|
|
@@ -551,23 +605,26 @@ export class LivewareSampleSupervisor {
|
|
|
551
605
|
if (this.bailIfStopped()) return;
|
|
552
606
|
const server = await startSampleServer({
|
|
553
607
|
appDir, port: row.port || DEFAULT_SAMPLE_PORT, spawnFn: deps.spawnFn,
|
|
608
|
+
agentUserId: deps.resolveAgentUserId?.() ?? null,
|
|
554
609
|
});
|
|
555
610
|
this.serverChild = server.child;
|
|
556
611
|
if (this.bailIfStopped()) return;
|
|
557
|
-
const
|
|
558
|
-
livewarePath, appId: row.app_id, port: server.port,
|
|
612
|
+
const publicUrl = await tunnelBind({
|
|
613
|
+
livewarePath, appId: row.app_id, port: server.port, execFileFn: deps.execFileFn,
|
|
559
614
|
});
|
|
560
|
-
this.tunnelChild = tunnel.child;
|
|
561
615
|
if (this.bailIfStopped()) return;
|
|
562
|
-
|
|
616
|
+
const agent = await startTunnelAgent({ livewarePath, spawnFn: deps.spawnFn });
|
|
617
|
+
this.tunnelChild = agent.child;
|
|
618
|
+
if (this.bailIfStopped()) return;
|
|
619
|
+
if (publicUrl !== row.public_url) {
|
|
563
620
|
await deps.registerApp({
|
|
564
|
-
name: row.app_name, appId: row.app_id, url:
|
|
621
|
+
name: row.app_name, appId: row.app_id, url: publicUrl,
|
|
565
622
|
});
|
|
566
623
|
}
|
|
567
624
|
deps.store.upsertLivewareSample({
|
|
568
625
|
platform: deps.platform, accountId: deps.accountId,
|
|
569
626
|
appId: row.app_id, appName: row.app_name, port: server.port,
|
|
570
|
-
publicUrl
|
|
627
|
+
publicUrl, sampleVersion: version, status: "active",
|
|
571
628
|
});
|
|
572
629
|
this.watchChildren();
|
|
573
630
|
if (row.intro_sent === 0) await this.deliverIntro();
|
package/src/runtime.ts
CHANGED
|
@@ -1119,6 +1119,7 @@ export async function startOpenclawClawlingGateway(params: StartGatewayParams):
|
|
|
1119
1119
|
store: store as unknown as LivewareSampleStoreLike,
|
|
1120
1120
|
sampleRoot: path.join(stateDirForSample ?? "", "clawchat", "liveware-sample"),
|
|
1121
1121
|
resolveToken: () => account.token ?? "",
|
|
1122
|
+
resolveAgentUserId: () => account.userId ?? null,
|
|
1122
1123
|
resolveLivewarePath: () => resolveLivewarePath(stateDirForSample),
|
|
1123
1124
|
cliReady: livewareCliReady,
|
|
1124
1125
|
listApps: async () => {
|